gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] 01/02: more exchange API refactoring


From: gnunet
Subject: [taler-exchange] 01/02: more exchange API refactoring
Date: Tue, 04 Jul 2023 23:28:13 +0200

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

grothoff pushed a commit to branch master
in repository exchange.

commit ff8349e6e7b7a898f3a9b0f69c44fa4a2a6a3fb1
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Tue Jul 4 15:37:34 2023 +0200

    more exchange API refactoring
---
 src/exchange-tools/taler-auditor-offline.c         |   30 +-
 src/include/taler_exchange_service.h               |  240 +--
 src/include/taler_testing_lib.h                    |   22 -
 src/lib/exchange_api_handle.c                      | 1588 +++++++-------------
 src/lib/exchange_api_handle.h                      |  174 +--
 src/testing/test_auditor_api.c                     |    2 +-
 src/testing/test_exchange_api.c                    |    2 +-
 .../test_exchange_api_keys_cherry_picking.c        |    2 +-
 .../test_exchange_api_overlapping_keys_bug.c       |    2 +-
 src/testing/test_exchange_api_revocation.c         |    2 +-
 src/testing/test_exchange_management_api.c         |    2 +-
 src/testing/test_exchange_p2p.c                    |    2 +-
 src/testing/test_kyc_api.c                         |    2 +-
 src/testing/test_taler_exchange_wirewatch.c        |    2 +-
 .../testing_api_cmd_auditor_deposit_confirmation.c |    6 +-
 src/testing/testing_api_cmd_batch_withdraw.c       |    8 +-
 src/testing/testing_api_cmd_check_keys.c           |  118 +-
 src/testing/testing_api_cmd_connect_with_state.c   |   32 +-
 src/testing/testing_api_cmd_get_exchange.c         |   41 +-
 src/testing/testing_api_cmd_purse_deposit.c        |    7 +-
 src/testing/testing_api_cmd_refresh.c              |   10 +-
 src/testing/testing_api_cmd_reserve_history.c      |   12 +-
 src/testing/testing_api_cmd_reserve_open.c         |    4 -
 src/testing/testing_api_cmd_reserve_purse.c        |    4 -
 src/testing/testing_api_cmd_reserve_status.c       |    4 -
 src/testing/testing_api_cmd_serialize_keys.c       |   10 +-
 src/testing/testing_api_cmd_transfer_get.c         |    4 -
 src/testing/testing_api_cmd_withdraw.c             |    6 +-
 src/testing/testing_api_traits.c                   |   27 -
 29 files changed, 740 insertions(+), 1625 deletions(-)

diff --git a/src/exchange-tools/taler-auditor-offline.c 
b/src/exchange-tools/taler-auditor-offline.c
index 39495311..38260abc 100644
--- a/src/exchange-tools/taler-auditor-offline.c
+++ b/src/exchange-tools/taler-auditor-offline.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2020-2021 Taler Systems SA
+  Copyright (C) 2020-2023 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
@@ -174,7 +174,7 @@ static struct DenominationAddRequest *dar_tail;
 /**
  * Handle to the exchange, used to request /keys.
  */
-static struct TALER_EXCHANGE_Handle *exchange;
+static struct TALER_EXCHANGE_GetKeysHandle *exchange;
 
 
 /**
@@ -219,7 +219,7 @@ do_shutdown (void *cls)
   }
   if (NULL != exchange)
   {
-    TALER_EXCHANGE_disconnect (exchange);
+    TALER_EXCHANGE_get_keys_cancel (exchange);
     exchange = NULL;
   }
   if (NULL != nxt)
@@ -646,22 +646,23 @@ do_upload (char *const *args)
  *
  * @param cls closure with the `char **` remaining args
  * @param kr response data
+ * @param keys key data from the exchange
  */
 static void
 keys_cb (
   void *cls,
-  const struct TALER_EXCHANGE_KeysResponse *kr)
+  const struct TALER_EXCHANGE_KeysResponse *kr,
+  struct TALER_EXCHANGE_Keys *keys)
 {
   char *const *args = cls;
 
+  exchange = NULL;
   switch (kr->hr.http_status)
   {
   case MHD_HTTP_OK:
-    if (! json_is_object (kr->hr.reply))
+    if (NULL == kr->hr.reply)
     {
       GNUNET_break (0);
-      TALER_EXCHANGE_disconnect (exchange);
-      exchange = NULL;
       test_shutdown ();
       global_ret = EXIT_FAILURE;
       return;
@@ -673,8 +674,6 @@ keys_cb (
              kr->hr.hint,
              kr->hr.http_status,
              (unsigned int) kr->hr.ec);
-    TALER_EXCHANGE_disconnect (exchange);
-    exchange = NULL;
     test_shutdown ();
     global_ret = EXIT_FAILURE;
     return;
@@ -692,9 +691,8 @@ keys_cb (
     json_decref (in);
     in = NULL;
   }
-  TALER_EXCHANGE_disconnect (exchange);
-  exchange = NULL;
   next (args);
+  TALER_EXCHANGE_keys_decref (keys);
 }
 
 
@@ -721,11 +719,11 @@ do_download (char *const *args)
     global_ret = EXIT_NOTCONFIGURED;
     return;
   }
-  exchange = TALER_EXCHANGE_connect (ctx,
-                                     exchange_url,
-                                     &keys_cb,
-                                     (void *) args,
-                                     TALER_EXCHANGE_OPTION_END);
+  exchange = TALER_EXCHANGE_get_keys (ctx,
+                                      exchange_url,
+                                      NULL,
+                                      &keys_cb,
+                                      (void *) args);
   GNUNET_free (exchange_url);
 }
 
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index dbc591a6..35a68872 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -32,27 +32,6 @@
 
 /* *********************  /keys *********************** */
 
-/**
- * List of possible options to be passed to
- * #TALER_EXCHANGE_connect().
- */
-enum TALER_EXCHANGE_Option
-{
-  /**
-   * Terminator (end of option list).
-   */
-  TALER_EXCHANGE_OPTION_END = 0,
-
-  /**
-   * Followed by a "const json_t *" that was previously returned for
-   * this exchange URL by #TALER_EXCHANGE_serialize_data().  Used to
-   * resume a connection to an exchange without having to re-download
-   * /keys data (or at least only download the deltas).
-   */
-  TALER_EXCHANGE_OPTION_DATA
-
-};
-
 
 /**
  * @brief Exchange's signature key
@@ -294,11 +273,18 @@ struct TALER_EXCHANGE_Keys
   char *currency;
 
   /**
-   * How long after a reserve went idle will the exchange close it?
-   * This is an approximate number, not cryptographically signed by
-   * the exchange (advisory-only, may change anytime).
+   * What is the base URL of the exchange that returned
+   * these keys?
    */
-  struct GNUNET_TIME_Relative reserve_closing_delay;
+  char *exchange_url;
+
+  /**
+   * Asset type used by the exchange. Typical values
+   * are "fiat" or "crypto" or "regional" or "stock".
+   * Wallets should adjust their UI/UX based on this
+   * value.
+   */
+  char *asset_type;
 
   /**
    * Array of amounts a wallet is allowed to hold from
@@ -307,16 +293,22 @@ struct TALER_EXCHANGE_Keys
   struct TALER_Amount *wallet_balance_limit_without_kyc;
 
   /**
-   * Length of the @e wallet_balance_limit_without_kyc
-   * array.
+   * How long after a reserve went idle will the exchange close it?
+   * This is an approximate number, not cryptographically signed by
+   * the exchange (advisory-only, may change anytime).
    */
-  unsigned int wblwk_length;
+  struct GNUNET_TIME_Relative reserve_closing_delay;
 
   /**
    * Timestamp indicating the /keys generation.
    */
   struct GNUNET_TIME_Timestamp list_issue_date;
 
+  /**
+   * When does this keys data expire?
+   */
+  struct GNUNET_TIME_Timestamp key_data_expiration;
+
   /**
    * Timestamp indicating the creation time of the last
    * denomination key in /keys.
@@ -329,6 +321,12 @@ struct TALER_EXCHANGE_Keys
    */
   struct TALER_AgeMask age_mask;
 
+  /**
+   * Length of the @e wallet_balance_limit_without_kyc
+   * array.
+   */
+  unsigned int wblwk_length;
+
   /**
    * Length of the @e global_fees array.
    */
@@ -360,12 +358,10 @@ struct TALER_EXCHANGE_Keys
   unsigned int denom_keys_size;
 
   /**
-   * Asset type used by the exchange. Typical values
-   * are "fiat" or "crypto" or "regional" or "stock".
-   * Wallets should adjust their UI/UX based on this
-   * value.
+   * Reference counter for this structure.
+   * Freed when it reaches 0.
    */
-  char *asset_type;
+  unsigned int rc;
 
   /**
    * Set to true if tipping is allowed at this exchange.
@@ -505,77 +501,82 @@ struct TALER_EXCHANGE_KeysResponse
 /**
  * Function called with information about who is auditing
  * a particular exchange and what keys the exchange is using.
+ * The ownership over the @a keys object is passed to
+ * the callee, thus it is given explicitly and not
+ * (only) via @a kr.
  *
  * @param cls closure
  * @param kr response from /keys
+ * @param[in] keys keys object passed to callback with
+ *  reference counter of 1. Must be freed by callee
+ *  using #TALER_EXCHANGE_keys_decref(). NULL on failure.
  */
 typedef void
-(*TALER_EXCHANGE_CertificationCallback) (
+(*TALER_EXCHANGE_GetKeysCallback) (
   void *cls,
-  const struct TALER_EXCHANGE_KeysResponse *kr);
+  const struct TALER_EXCHANGE_KeysResponse *kr,
+  struct TALER_EXCHANGE_Keys *keys);
 
 
 /**
- * @brief Handle to the exchange.  This is where we interact with
- * a particular exchange and keep the per-exchange information.
+ * @brief Handle for a GET /keys request.
  */
-struct TALER_EXCHANGE_Handle;
+struct TALER_EXCHANGE_GetKeysHandle;
 
 
 /**
- * Initialise a connection to the exchange.  Will connect to the
- * exchange and obtain information about the exchange's master public
- * key and the exchange's auditor.  The respective information will
- * be passed to the @a cert_cb once available, and all future
- * interactions with the exchange will be checked to be signed
- * (where appropriate) by the respective master key.
+ * Fetch the main /keys resources from ane exchange.  Does an incremental
+ * fetch if @a last_keys is given.  The obtained information will be passed to
+ * the @a cert_cb (possibly after first merging it with @a last_keys to
+ * produce a full picture; expired keys (for deposit) will be removed from @a
+ * last_keys if there are any).
  *
  * @param ctx the context
  * @param url HTTP base URL for the exchange
+ * @param[in,out] last_keys previous keys object, NULL for none
  * @param cert_cb function to call with the exchange's certification 
information,
  *                possibly called repeatedly if the information changes
  * @param cert_cb_cls closure for @a cert_cb
- * @param ... list of additional arguments, terminated by 
#TALER_EXCHANGE_OPTION_END.
  * @return the exchange handle; NULL upon error
  */
-struct TALER_EXCHANGE_Handle *
-TALER_EXCHANGE_connect (struct GNUNET_CURL_Context *ctx,
-                        const char *url,
-                        TALER_EXCHANGE_CertificationCallback cert_cb,
-                        void *cert_cb_cls,
-                        ...);
+struct TALER_EXCHANGE_GetKeysHandle *
+TALER_EXCHANGE_get_keys (
+  struct GNUNET_CURL_Context *ctx,
+  const char *url,
+  struct TALER_EXCHANGE_Keys *last_keys,
+  TALER_EXCHANGE_GetKeysCallback cert_cb,
+  void *cert_cb_cls);
 
 
 /**
- * Serialize the latest key data from @a exchange to be persisted
- * on disk (to be used with #TALER_EXCHANGE_OPTION_DATA to more
- * efficiently recover the state).
+ * Serialize the latest data from @a keys to be persisted
+ * (for example, to be used as @a last_keys later).
  *
- * @param exchange which exchange's key and wire data should be serialized
- * @return NULL on error (i.e. no current data available); otherwise
- *         json object owned by the caller
+ * @param kd the key data to serialize
+ * @return NULL on error; otherwise JSON object owned by the caller
  */
 json_t *
-TALER_EXCHANGE_serialize_data (struct TALER_EXCHANGE_Handle *exchange);
+TALER_EXCHANGE_keys_to_json (const struct TALER_EXCHANGE_Keys *kd);
 
 
 /**
- * Disconnect from the exchange.
+ * Deserialize keys data stored in @a j.
  *
- * @param exchange the exchange handle
+ * @param j JSON keys data previously returned from 
#TALER_EXCHANGE_keys_to_json()
+ * @return NULL on error (i.e. invalid JSON); otherwise
+ *         keys object with reference counter 1 owned by the caller
  */
-void
-TALER_EXCHANGE_disconnect (struct TALER_EXCHANGE_Handle *exchange);
+struct TALER_EXCHANGE_Keys *
+TALER_EXCHANGE_keys_from_json (const json_t *j);
 
 
 /**
- * Obtain the keys from the exchange.
+ * Cancel GET /keys operation.
  *
- * @param exchange the exchange handle
- * @return the exchange's key set
+ * @param[in] gkh the GET /keys handle
  */
-struct TALER_EXCHANGE_Keys *
-TALER_EXCHANGE_get_keys (struct TALER_EXCHANGE_Handle *exchange);
+void
+TALER_EXCHANGE_get_keys_cancel (struct TALER_EXCHANGE_GetKeysHandle *gkh);
 
 
 /**
@@ -598,90 +599,6 @@ void
 TALER_EXCHANGE_keys_decref (struct TALER_EXCHANGE_Keys *keys);
 
 
-/**
- * Let the user set the last valid denomination time manually.
- *
- * @param exchange the exchange handle.
- * @param last_denom_new new last denomination time.
- */
-void
-TALER_EXCHANGE_set_last_denom (
-  struct TALER_EXCHANGE_Handle *exchange,
-  struct GNUNET_TIME_Timestamp last_denom_new);
-
-
-/**
- * Flags for #TALER_EXCHANGE_check_keys_current().
- */
-enum TALER_EXCHANGE_CheckKeysFlags
-{
-  /**
-   * No special options.
-   */
-  TALER_EXCHANGE_CKF_NONE,
-
-  /**
-   * Force downloading /keys now, even if /keys is still valid
-   * (that is, the period advertised by the exchange for re-downloads
-   * has not yet expired).
-   */
-  TALER_EXCHANGE_CKF_FORCE_DOWNLOAD = 1,
-
-  /**
-   * Pull all keys again, resetting the client state to the original state.
-   * Using this flag disables the incremental download, and also prevents using
-   * the context until the re-download has completed.
-   */
-  TALER_EXCHANGE_CKF_PULL_ALL_KEYS = 2,
-
-  /**
-   * Force downloading all keys now.
-   */
-  TALER_EXCHANGE_CKF_FORCE_ALL_NOW = TALER_EXCHANGE_CKF_FORCE_DOWNLOAD
-                                     | TALER_EXCHANGE_CKF_PULL_ALL_KEYS
-
-};
-
-
-/**
- * Check if our current response for /keys is valid, and if
- * not, trigger /keys download.  If @a cb is given, changes
- * the @a exchange callback for the /keys response.
- *
- * @param exchange exchange to check keys for
- * @param flags options controlling when to download what
- * @param cb function to call with the /keys response, can be NULL
- * @param cb_cls closure for @a cb
- * @return until when the existing response is current, 0 if we are 
re-downloading now
- */
-struct GNUNET_TIME_Timestamp
-TALER_EXCHANGE_check_keys_current (
-  struct TALER_EXCHANGE_Handle *exchange,
-  enum TALER_EXCHANGE_CheckKeysFlags flags,
-  TALER_EXCHANGE_CertificationCallback cb,
-  void *cb_cls);
-
-
-/**
- * Obtain the keys from the exchange in the raw JSON format.
- *
- * @param exchange the exchange handle
- * @return the exchange's keys in raw JSON
- */
-json_t *
-TALER_EXCHANGE_get_keys_raw (struct TALER_EXCHANGE_Handle *exchange);
-
-
-/**
- * Obtain the keys from the exchange in the raw JSON format.
- *
- * @param keys the keys structure
- * @return the keys in raw JSON
- */
-json_t *
-TALER_EXCHANGE_keys_to_json (struct TALER_EXCHANGE_Keys *keys);
-
-
 /**
  * Test if the given @a pub is a the current signing key from the exchange
  * according to @a keys.
@@ -691,18 +608,9 @@ TALER_EXCHANGE_keys_to_json (struct TALER_EXCHANGE_Keys 
*keys);
  * @return #GNUNET_OK if @a pub is (according to /keys) a current signing key
  */
 enum GNUNET_GenericReturnValue
-TALER_EXCHANGE_test_signing_key (const struct TALER_EXCHANGE_Keys *keys,
-                                 const struct TALER_ExchangePublicKeyP *pub);
-
-
-/**
- * Get exchange's base URL.
- *
- * @param exchange exchange handle.
- * @return the base URL from the handle.
- */
-const char *
-TALER_EXCHANGE_get_base_url (const struct TALER_EXCHANGE_Handle *exchange);
+TALER_EXCHANGE_test_signing_key (
+  const struct TALER_EXCHANGE_Keys *keys,
+  const struct TALER_ExchangePublicKeyP *pub);
 
 
 /**
@@ -736,7 +644,8 @@ TALER_EXCHANGE_get_global_fee (
  * Create a copy of a denomination public key.
  *
  * @param key key to copy
- * @returns a copy, must be freed with #TALER_EXCHANGE_destroy_denomination_key
+ * @returns a copy, must be freed with 
#TALER_EXCHANGE_destroy_denomination_key()
+ * @deprecated
  */
 struct TALER_EXCHANGE_DenomPublicKey *
 TALER_EXCHANGE_copy_denomination_key (
@@ -745,9 +654,10 @@ TALER_EXCHANGE_copy_denomination_key (
 
 /**
  * Destroy a denomination public key.
- * Should only be called with keys created by 
#TALER_EXCHANGE_copy_denomination_key.
+ * Should only be called with keys created by 
#TALER_EXCHANGE_copy_denomination_key().
  *
  * @param key key to destroy.
+ * @deprecated
  */
 void
 TALER_EXCHANGE_destroy_denomination_key (
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index 8950e71b..332c3a34 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -1797,17 +1797,6 @@ struct TALER_TESTING_Command
 TALER_TESTING_cmd_check_keys (const char *label);
 
 
-/**
- * Make a "check keys" command that forcedly does NOT cherry pick;
- * just redownload the whole /keys.
- *
- * @param label command label
- * @return the command.
- */
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_check_keys_pull_all_keys (const char *label);
-
-
 /**
  * Make a "check keys" command.  It lets the user set a last denom issue date 
to be
  * used in the request for /keys.
@@ -2703,7 +2692,6 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait 
*traits,
   op (fresh_coins, const struct TALER_TESTING_FreshCoinData *)     \
   op (claim_token, const struct TALER_ClaimTokenP)                 \
   op (relative_time, const struct GNUNET_TIME_Relative)            \
-  op (exchange, struct TALER_EXCHANGE_Handle)                      \
   op (fakebank, struct TALER_FAKEBANK_Handle)                      \
   op (keys, struct TALER_EXCHANGE_Keys)                            \
   op (process, struct GNUNET_OS_Process *)
@@ -2742,16 +2730,6 @@ TALER_TESTING_INDEXED_TRAITS 
(TALER_TESTING_MAKE_DECL_INDEXED_TRAIT)
 
 /* ****************** convenience functions ************** */
 
-/**
- * Get exchange handle from interpreter. Convenience function.
- *
- * @param is interpreter state.
- * @return the exchange handle, or NULL on error
- */
-struct TALER_EXCHANGE_Handle *
-TALER_TESTING_get_exchange (struct TALER_TESTING_Interpreter *is);
-
-
 /**
  * Get exchange URL from interpreter. Convenience function.
  *
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index 0bb3c208..7fcb590a 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -47,6 +47,11 @@
  */
 #define EXCHANGE_PROTOCOL_AGE 0
 
+/**
+ * Set to 1 for extra debug logging.
+ */
+#define DEBUG 0
+
 /**
  * Current version for (local) JSON serialization of persisted
  * /keys data.
@@ -64,103 +69,58 @@
  */
 #define DEFAULT_EXPIRATION GNUNET_TIME_UNIT_HOURS
 
-/**
- * Set to 1 for extra debug logging.
- */
-#define DEBUG 0
-
-/**
- * Log error related to CURL operations.
- *
- * @param type log level
- * @param function which function failed to run
- * @param code what was the curl error code
- */
-#define CURL_STRERROR(type, function, code)      \
-  GNUNET_log (type, "Curl function `%s' has failed at `%s:%d' with error: %s", 
\
-              function, __FILE__, __LINE__, curl_easy_strerror (code));
-
-
-/**
- * Data for the request to get the /keys of a exchange.
- */
-struct KeysRequest;
-
 
 /**
- * Entry in DLL of auditors used by an exchange.
+ * Handle for a GET /keys request.
  */
-struct TEAH_AuditorListEntry
+struct TALER_EXCHANGE_GetKeysHandle
 {
-  /**
-   * Next pointer of DLL.
-   */
-  struct TEAH_AuditorListEntry *next;
-
-  /**
-   * Prev pointer of DLL.
-   */
-  struct TEAH_AuditorListEntry *prev;
-
-  /**
-   * Base URL of the auditor.
-   */
-  char *auditor_url;
 
   /**
-   * Handle to the auditor.
+   * The exchange base URL (i.e. "http://exchange.taler.net/";)
    */
-  struct TALER_AUDITOR_GetConfigHandle *ah;
+  char *exchange_url;
 
   /**
-   * Public key of the auditor.
+   * The url for the /keys request.
    */
-  struct TALER_AuditorPublicKeyP auditor_pub;
+  char *url;
 
   /**
-   * Flag indicating that the auditor is available and that protocol
-   * version compatibility is given.
+   * Previous /keys response, NULL for none.
    */
-  bool is_up;
-
-};
-
+  struct TALER_EXCHANGE_Keys *prev_keys;
 
-/* ***************** Internal /keys fetching ************* */
-
-/**
- * Data for the request to get the /keys of a exchange.
- */
-struct KeysRequest
-{
   /**
-   * The connection to exchange this request handle will use
+   * Entry for this request with the `struct GNUNET_CURL_Context`.
    */
-  struct TALER_EXCHANGE_Handle *exchange;
+  struct GNUNET_CURL_Job *job;
 
   /**
-   * The url for this handle
+   * Expiration time according to "Expire:" header.
+   * 0 if not provided by the server.
    */
-  char *url;
+  struct GNUNET_TIME_Timestamp expire;
 
   /**
-   * Entry for this request with the `struct GNUNET_CURL_Context`.
+   * Function to call with the exchange's certification data,
+   * NULL if this has already been done.
    */
-  struct GNUNET_CURL_Job *job;
+  TALER_EXCHANGE_GetKeysCallback cert_cb;
 
   /**
-   * Expiration time according to "Expire:" header.
-   * 0 if not provided by the server.
+   * Closure to pass to @e cert_cb.
    */
-  struct GNUNET_TIME_Timestamp expire;
+  void *cert_cb_cls;
 
 };
 
 
 void
-TEAH_get_auditors_for_dc (struct TALER_EXCHANGE_Keys *keys,
-                          TEAH_AuditorCallback ac,
-                          void *ac_cls)
+TEAH_get_auditors_for_dc (
+  struct TALER_EXCHANGE_Keys *keys,
+  TEAH_AuditorCallback ac,
+  void *ac_cls)
 {
   if (0 == keys->num_auditors)
   {
@@ -180,20 +140,6 @@ TEAH_get_auditors_for_dc (struct TALER_EXCHANGE_Keys *keys,
 }
 
 
-/**
- * Release memory occupied by a keys request.  Note that this does not
- * cancel the request itself.
- *
- * @param kr request to free
- */
-static void
-free_keys_request (struct KeysRequest *kr)
-{
-  GNUNET_free (kr->url);
-  GNUNET_free (kr);
-}
-
-
 #define EXITIF(cond)                                              \
   do {                                                            \
     if (cond) { GNUNET_break (0); goto EXITIF_exit; }             \
@@ -205,21 +151,20 @@ free_keys_request (struct KeysRequest *kr)
  *
  * @param[out] sign_key where to return the result
  * @param check_sigs should we check signatures?
- * @param[in] sign_key_obj json to parse
+ * @param sign_key_obj json to parse
  * @param master_key master key to use to verify signature
  * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
- *        invalid or the json malformed.
+ *        invalid or the @a sign_key_obj is malformed.
  */
 static enum GNUNET_GenericReturnValue
 parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key,
                     bool check_sigs,
-                    json_t *sign_key_obj,
+                    const json_t *sign_key_obj,
                     const struct TALER_MasterPublicKeyP *master_key)
 {
-  struct TALER_MasterSignatureP sign_key_issue_sig;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("master_sig",
-                                 &sign_key_issue_sig),
+                                 &sign_key->master_sig),
     GNUNET_JSON_spec_fixed_auto ("key",
                                  &sign_key->key),
     GNUNET_JSON_spec_timestamp ("stamp_start",
@@ -239,7 +184,6 @@ parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey 
*sign_key,
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
-
   if (! check_sigs)
     return GNUNET_OK;
   if (GNUNET_OK !=
@@ -249,12 +193,11 @@ parse_json_signkey (struct 
TALER_EXCHANGE_SigningPublicKey *sign_key,
         sign_key->valid_until,
         sign_key->valid_legal,
         master_key,
-        &sign_key_issue_sig))
+        &sign_key->master_sig))
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
-  sign_key->master_sig = sign_key_issue_sig;
   return GNUNET_OK;
 }
 
@@ -270,9 +213,9 @@ parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey 
*sign_key,
  * @param[out] denom_key where to return the result
  * @param cipher cipher type to parse
  * @param check_sigs should we check signatures?
- * @param[in] denom_key_obj json to parse
+ * @param denom_key_obj json to parse
  * @param master_key master key to use to verify signature
- * @param hash_xor where to accumulate data for signature verification via XOR
+ * @param[in,out] hash_xor where to accumulate data for signature verification 
via XOR
  * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
  *        invalid or the json malformed.
  */
@@ -281,7 +224,7 @@ parse_json_denomkey_partially (
   struct TALER_EXCHANGE_DenomPublicKey *denom_key,
   enum TALER_DenominationCipher cipher,
   bool check_sigs,
-  json_t *denom_key_obj,
+  const json_t *denom_key_obj,
   struct TALER_MasterPublicKeyP *master_key,
   struct GNUNET_HashCode *hash_xor)
 {
@@ -316,7 +259,6 @@ parse_json_denomkey_partially (
     GNUNET_CRYPTO_hash_xor (&denom_key->h_key.hash,
                             hash_xor,
                             hash_xor);
-
   if (! check_sigs)
     return GNUNET_OK;
   EXITIF (GNUNET_SYSERR ==
@@ -346,7 +288,7 @@ EXITIF_exit:
  *
  * @param[out] auditor where to return the result
  * @param check_sigs should we check signatures
- * @param[in] auditor_obj json to parse
+ * @param auditor_obj json to parse
  * @param key_data information about denomination keys
  * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
  *        invalid or the json malformed.
@@ -354,14 +296,12 @@ EXITIF_exit:
 static enum GNUNET_GenericReturnValue
 parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor,
                     bool check_sigs,
-                    json_t *auditor_obj,
+                    const json_t *auditor_obj,
                     const struct TALER_EXCHANGE_Keys *key_data)
 {
   const json_t *keys;
   json_t *key;
-  unsigned int len;
   unsigned int off;
-  unsigned int i;
   const char *auditor_url;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("auditor_pub",
@@ -387,16 +327,15 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
     return GNUNET_SYSERR;
   }
   auditor->auditor_url = GNUNET_strdup (auditor_url);
-  len = json_array_size (keys);
-  auditor->denom_keys = GNUNET_new_array (len,
-                                          struct
-                                          
TALER_EXCHANGE_AuditorDenominationInfo);
-  off = 0;
-  json_array_foreach (keys, i, key) {
+  auditor->denom_keys
+    = GNUNET_new_array (json_array_size (keys),
+                        struct TALER_EXCHANGE_AuditorDenominationInfo);
+
+  json_array_foreach (keys, off, key) {
     struct TALER_AuditorSignatureP auditor_sig;
     struct TALER_DenominationHashP denom_h;
-    const struct TALER_EXCHANGE_DenomPublicKey *dk;
-    unsigned int dk_off;
+    const struct TALER_EXCHANGE_DenomPublicKey *dk = NULL;
+    unsigned int dk_off = UINT_MAX;
     struct GNUNET_JSON_Specification kspec[] = {
       GNUNET_JSON_spec_fixed_auto ("auditor_sig",
                                    &auditor_sig),
@@ -413,8 +352,6 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
       GNUNET_break_op (0);
       continue;
     }
-    dk = NULL;
-    dk_off = UINT_MAX;
     for (unsigned int j = 0; j<key_data->num_denom_keys; j++)
     {
       if (0 == GNUNET_memcmp (&denom_h,
@@ -454,7 +391,6 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
     }
     auditor->denom_keys[off].denom_key_offset = dk_off;
     auditor->denom_keys[off].auditor_sig = auditor_sig;
-    off++;
   }
   auditor->num_denom_keys = off;
   return GNUNET_OK;
@@ -466,7 +402,7 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
  *
  * @param[out] gf where to return the result
  * @param check_sigs should we check signatures
- * @param[in] fee_obj json to parse
+ * @param fee_obj json to parse
  * @param key_data already parsed information about the exchange
  * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
  *        invalid or the json malformed.
@@ -474,7 +410,7 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
 static enum GNUNET_GenericReturnValue
 parse_global_fee (struct TALER_EXCHANGE_GlobalFee *gf,
                   bool check_sigs,
-                  json_t *fee_obj,
+                  const json_t *fee_obj,
                   const struct TALER_EXCHANGE_Keys *key_data)
 {
   struct GNUNET_JSON_Specification spec[] = {
@@ -531,104 +467,6 @@ parse_global_fee (struct TALER_EXCHANGE_GlobalFee *gf,
 }
 
 
-/**
- * Function called with information about the auditor.  Marks an
- * auditor as 'up'.
- *
- * @param cls closure, a `struct TEAH_AuditorListEntry *`
- * @param vr response from the auditor
- */
-static void
-auditor_config_cb (
-  void *cls,
-  const struct TALER_AUDITOR_ConfigResponse *vr)
-{
-  struct TEAH_AuditorListEntry *ale = cls;
-  enum TALER_AUDITOR_VersionCompatibility compat;
-
-  ale->ah = NULL;
-  if (MHD_HTTP_OK != vr->hr.http_status)
-  {
-    /* In this case, we don't mark the auditor as 'up' */
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Auditor `%s' gave unexpected version response.\n",
-                ale->auditor_url);
-    return;
-  }
-  compat = vr->details.ok.compat;
-  if (0 != (TALER_AUDITOR_VC_INCOMPATIBLE & compat))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Auditor `%s' runs incompatible protocol version!\n",
-                ale->auditor_url);
-    if (0 != (TALER_AUDITOR_VC_OLDER & compat))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Auditor `%s' runs outdated protocol version!\n",
-                  ale->auditor_url);
-    }
-    if (0 != (TALER_AUDITOR_VC_NEWER & compat))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Auditor `%s' runs more recent incompatible version. We 
should upgrade!\n",
-                  ale->auditor_url);
-    }
-    return;
-  }
-  ale->is_up = true;
-}
-
-
-/**
- * Recalculate our auditor list, we got /keys and it may have
- * changed.
- *
- * @param exchange exchange for which to update the list.
- */
-static void
-update_auditors (struct TALER_EXCHANGE_Handle *exchange)
-{
-  struct TALER_EXCHANGE_Keys *kd = &exchange->key_data;
-
-  TALER_LOG_DEBUG ("Updating auditors\n");
-  for (unsigned int i = 0; i<kd->num_auditors; i++)
-  {
-    /* Compare auditor data from /keys with auditor data
-     * from owned exchange structures.  */
-    struct TALER_EXCHANGE_AuditorInformation *auditor = &kd->auditors[i];
-    struct TEAH_AuditorListEntry *ale = NULL;
-
-    for (struct TEAH_AuditorListEntry *a = exchange->auditors_head;
-         NULL != a;
-         a = a->next)
-    {
-      if (0 == GNUNET_memcmp (&auditor->auditor_pub,
-                              &a->auditor_pub))
-      {
-        ale = a;
-        break;
-      }
-    }
-    if (NULL != ale)
-      continue; /* found, no need to add */
-
-    /* new auditor, add */
-    TALER_LOG_DEBUG ("Found new auditor %s!\n",
-                     auditor->auditor_url);
-    ale = GNUNET_new (struct TEAH_AuditorListEntry);
-    ale->auditor_pub = auditor->auditor_pub;
-    ale->auditor_url = GNUNET_strdup (auditor->auditor_url);
-    GNUNET_CONTAINER_DLL_insert (exchange->auditors_head,
-                                 exchange->auditors_tail,
-                                 ale);
-    ale->ah = TALER_AUDITOR_get_config (exchange->ctx,
-                                        ale->auditor_url,
-                                        &auditor_config_cb,
-                                        ale);
-  }
-}
-
-
 /**
  * Compare two denomination keys.  Ignores revocation data.
  *
@@ -683,35 +521,16 @@ decode_keys_json (const json_t *resp_obj,
   struct TALER_ExchangeSignatureP denominations_sig;
   struct GNUNET_HashCode hash_xor = {0};
   struct TALER_ExchangePublicKeyP pub;
-  const char *currency;
-  const char *asset_type;
-  bool tipping_allowed = true;
   const json_t *wblwk = NULL;
-  struct GNUNET_JSON_Specification mspec[] = {
-    GNUNET_JSON_spec_fixed_auto ("denominations_sig",
-                                 &denominations_sig),
-    GNUNET_JSON_spec_fixed_auto ("eddsa_pub",
-                                 &pub),
-    GNUNET_JSON_spec_fixed_auto ("master_public_key",
-                                 &key_data->master_pub),
-    GNUNET_JSON_spec_timestamp ("list_issue_date",
-                                &key_data->list_issue_date),
-    GNUNET_JSON_spec_relative_time ("reserve_closing_delay",
-                                    &key_data->reserve_closing_delay),
-    GNUNET_JSON_spec_string ("currency",
-                             &currency),
-    GNUNET_JSON_spec_string ("asset_type",
-                             &asset_type),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_bool ("tipping_allowed",
-                             &tipping_allowed),
-      NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_array_const ("wallet_balance_limit_without_kyc",
-                                    &wblwk),
-      NULL),
-    GNUNET_JSON_spec_end ()
-  };
+  const json_t *global_fees;
+  const json_t *sign_keys_array;
+  const json_t *denominations_by_group;
+  const json_t *auditors_array;
+  const json_t *recoup_array = NULL;
+  struct TALER_MasterSignatureP extensions_sig = {0};
+  const json_t *manifests = NULL;
+  bool no_extensions = false;
+  bool no_signature = false;
 
   if (JSON_OBJECT != json_typeof (resp_obj))
   {
@@ -770,63 +589,116 @@ decode_keys_json (const json_t *resp_obj,
     key_data->version = GNUNET_strdup (ver);
   }
 
-  EXITIF (GNUNET_OK !=
-          GNUNET_JSON_parse (resp_obj,
-                             (check_sig) ? mspec : &mspec[2],
-                             NULL, NULL));
-  key_data->currency = GNUNET_strdup (currency);
-  key_data->asset_type = GNUNET_strdup (asset_type);
-  key_data->tipping_allowed = tipping_allowed;
+  {
+    const char *currency;
+    const char *asset_type;
+    struct GNUNET_JSON_Specification mspec[] = {
+      GNUNET_JSON_spec_fixed_auto (
+        "denominations_sig",
+        &denominations_sig),
+      GNUNET_JSON_spec_fixed_auto (
+        "eddsa_pub",
+        &pub),
+      GNUNET_JSON_spec_fixed_auto (
+        "master_public_key",
+        &key_data->master_pub),
+      GNUNET_JSON_spec_timestamp (
+        "list_issue_date",
+        &key_data->list_issue_date),
+      GNUNET_JSON_spec_relative_time (
+        "reserve_closing_delay",
+        &key_data->reserve_closing_delay),
+      GNUNET_JSON_spec_string (
+        "currency",
+        &currency),
+      GNUNET_JSON_spec_string (
+        "asset_type",
+        &asset_type),
+      GNUNET_JSON_spec_array_const (
+        "global_fees",
+        &global_fees),
+      GNUNET_JSON_spec_array_const (
+        "signkeys",
+        &sign_keys_array),
+      GNUNET_JSON_spec_array_const (
+        "denominations",
+        &denominations_by_group),
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_array_const (
+          "recoup",
+          &recoup_array),
+        NULL),
+      GNUNET_JSON_spec_array_const (
+        "auditors",
+        &auditors_array),
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_bool (
+          "tipping_allowed",
+          &key_data->tipping_allowed),
+        NULL),
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_object_const ("extensions",
+                                       &manifests),
+        &no_extensions),
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_fixed_auto (
+          "extensions_sig",
+          &extensions_sig),
+        &no_signature),
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_array_const (
+          "wallet_balance_limit_without_kyc",
+          &wblwk),
+        NULL),
+      GNUNET_JSON_spec_end ()
+    };
+
+    EXITIF (GNUNET_OK !=
+            GNUNET_JSON_parse (resp_obj,
+                               (check_sig) ? mspec : &mspec[2],
+                               NULL, NULL));
+    key_data->currency = GNUNET_strdup (currency);
+    key_data->asset_type = GNUNET_strdup (asset_type);
+  }
 
   /* parse the global fees */
+  key_data->num_global_fees
+    = json_array_size (global_fees);
+  if (0 != key_data->num_global_fees)
   {
-    json_t *global_fees;
     json_t *global_fee;
     unsigned int index;
 
-    EXITIF (NULL == (global_fees =
-                       json_object_get (resp_obj,
-                                        "global_fees")));
-    EXITIF (! json_is_array (global_fees));
-    if (0 != (key_data->num_global_fees =
-                json_array_size (global_fees)))
+    key_data->global_fees
+      = GNUNET_new_array (key_data->num_global_fees,
+                          struct TALER_EXCHANGE_GlobalFee);
+    json_array_foreach (global_fees, index, global_fee)
     {
-      key_data->global_fees
-        = GNUNET_new_array (key_data->num_global_fees,
-                            struct TALER_EXCHANGE_GlobalFee);
-      json_array_foreach (global_fees, index, global_fee) {
-        EXITIF (GNUNET_SYSERR ==
-                parse_global_fee (&key_data->global_fees[index],
-                                  check_sig,
-                                  global_fee,
-                                  key_data));
-      }
+      EXITIF (GNUNET_SYSERR ==
+              parse_global_fee (&key_data->global_fees[index],
+                                check_sig,
+                                global_fee,
+                                key_data));
     }
   }
 
   /* parse the signing keys */
+  key_data->num_sign_keys
+    = json_array_size (sign_keys_array);
+  if (0 != key_data->num_sign_keys)
   {
-    json_t *sign_keys_array;
     json_t *sign_key_obj;
     unsigned int index;
 
-    EXITIF (NULL == (sign_keys_array =
-                       json_object_get (resp_obj,
-                                        "signkeys")));
-    EXITIF (! json_is_array (sign_keys_array));
-    if (0 != (key_data->num_sign_keys =
-                json_array_size (sign_keys_array)))
-    {
-      key_data->sign_keys
-        = GNUNET_new_array (key_data->num_sign_keys,
-                            struct TALER_EXCHANGE_SigningPublicKey);
-      json_array_foreach (sign_keys_array, index, sign_key_obj) {
-        EXITIF (GNUNET_SYSERR ==
-                parse_json_signkey (&key_data->sign_keys[index],
-                                    check_sig,
-                                    sign_key_obj,
-                                    &key_data->master_pub));
-      }
+    key_data->sign_keys
+      = GNUNET_new_array (key_data->num_sign_keys,
+                          struct TALER_EXCHANGE_SigningPublicKey);
+    json_array_foreach (sign_keys_array, index, sign_key_obj) {
+      EXITIF (GNUNET_SYSERR ==
+              parse_json_signkey (&key_data->sign_keys[index],
+                                  check_sig,
+                                  sign_key_obj,
+                                  &key_data->master_pub));
     }
   }
 
@@ -844,7 +716,7 @@ decode_keys_json (const json_t *resp_obj,
                                          i);
       struct GNUNET_JSON_Specification spec[] = {
         TALER_JSON_spec_amount (NULL,
-                                currency,
+                                key_data->currency,
                                 a),
         GNUNET_JSON_spec_end ()
       };
@@ -858,51 +730,28 @@ decode_keys_json (const json_t *resp_obj,
 
   /* Parse the supported extension(s): age-restriction. */
   /* TODO: maybe lift all this into a FP in TALER_Extension ? */
+  if (! no_extensions)
   {
-    struct TALER_MasterSignatureP extensions_sig = {0};
-    const json_t *manifests = NULL;
-    bool no_extensions = false;
-    bool no_signature = false;
-
-    struct GNUNET_JSON_Specification ext_spec[] = {
-      GNUNET_JSON_spec_mark_optional (
-        GNUNET_JSON_spec_object_const ("extensions",
-                                       &manifests),
-        &no_extensions),
-      GNUNET_JSON_spec_mark_optional (
-        GNUNET_JSON_spec_fixed_auto (
-          "extensions_sig",
-          &extensions_sig),
-        &no_signature),
-      GNUNET_JSON_spec_end ()
-    };
-
-    /* 1. Search for extensions in the response to /keys */
-    EXITIF (GNUNET_OK !=
-            GNUNET_JSON_parse (resp_obj,
-                               ext_spec,
-                               NULL, NULL));
-
-
-    if (! no_extensions && no_signature)
+    if (no_signature)
+    {
       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                   "found extensions without signature\n");
-
-    if (! no_extensions && ! no_signature)
+    }
+    else
     {
-      /* 2. We have an extensions object. Verify its signature. */
+      /* We have an extensions object. Verify its signature. */
       EXITIF (GNUNET_OK !=
               TALER_extensions_verify_manifests_signature (
                 manifests,
                 &extensions_sig,
                 &key_data->master_pub));
 
-      /* 3. Parse and set the the configuration of the extensions accordingly 
*/
+      /* Parse and set the the configuration of the extensions accordingly */
       EXITIF (GNUNET_OK !=
               TALER_extensions_load_manifests (manifests));
     }
 
-    /* 4. assuming we might have now a new value for age_mask, set it in 
key_data */
+    /* Assuming we might have now a new value for age_mask, set it in key_data 
*/
     key_data->age_mask = TALER_extensions_get_age_restriction_mask ();
   }
 
@@ -912,21 +761,13 @@ decode_keys_json (const json_t *resp_obj,
    *
    * The denominations are grouped by common values of
    *    {cipher, value, fee, age_mask}.
-   **/
+   */
   {
-    json_t *denominations_by_group;
     json_t *group_obj;
     unsigned int group_idx;
 
-    denominations_by_group =
-      json_object_get (
-        resp_obj,
-        "denominations");
-
-    EXITIF (JSON_ARRAY !=
-            json_typeof (denominations_by_group));
-
-    json_array_foreach (denominations_by_group, group_idx, group_obj) {
+    json_array_foreach (denominations_by_group, group_idx, group_obj)
+    {
       /* Running XOR of each SHA512 hash of the denominations' public key in
          this group.  Used to compare against group.hash after all keys have
          been parsed. */
@@ -935,9 +776,13 @@ decode_keys_json (const json_t *resp_obj,
       /* First, parse { cipher, fees, value, age_mask, hash } of the current
          group. */
       struct TALER_DenominationGroup group = {0};
+      const json_t *denom_keys_array;
       struct GNUNET_JSON_Specification group_spec[] = {
         TALER_JSON_spec_denomination_group (NULL,
-                                            currency, &group),
+                                            key_data->currency,
+                                            &group),
+        GNUNET_JSON_spec_array_const ("denoms",
+                                      &denom_keys_array),
         GNUNET_JSON_spec_end ()
       };
       EXITIF (GNUNET_SYSERR ==
@@ -948,25 +793,21 @@ decode_keys_json (const json_t *resp_obj,
 
       /* Now, parse the individual denominations */
       {
-        json_t *denom_keys_array;
         json_t *denom_key_obj;
         unsigned int index;
-        denom_keys_array = json_object_get (group_obj, "denoms");
-        EXITIF (JSON_ARRAY != json_typeof (denom_keys_array));
-
-        json_array_foreach (denom_keys_array, index, denom_key_obj) {
-          struct TALER_EXCHANGE_DenomPublicKey dk = {0};
-          bool found = false;
-
-          memset (&dk, 0, sizeof (dk));
 
+        json_array_foreach (denom_keys_array, index, denom_key_obj)
+        {
           /* Set the common fields from the group for this particular
              denomination.  Required to make the validity check inside
              parse_json_denomkey_partially pass */
-          dk.key.cipher = group.cipher;
-          dk.value = group.value;
-          dk.fees = group.fees;
-          dk.key.age_mask = group.age_mask;
+          struct TALER_EXCHANGE_DenomPublicKey dk = {
+            .key.cipher = group.cipher,
+            .value = group.value,
+            .fees = group.fees,
+            .key.age_mask = group.age_mask
+          };
+          bool found = false;
 
           EXITIF (GNUNET_SYSERR ==
                   parse_json_denomkey_partially (&dk,
@@ -979,7 +820,9 @@ decode_keys_json (const json_t *resp_obj,
           /* Build the running xor of the SHA512-hash of the public keys */
           {
             struct TALER_DenominationHashP hc = {0};
-            TALER_denom_pub_hash (&dk.key, &hc);
+
+            TALER_denom_pub_hash (&dk.key,
+                                  &hc);
             GNUNET_CRYPTO_hash_xor (&hc.hash,
                                     &group_hash_xor,
                                     &group_hash_xor);
@@ -1017,30 +860,26 @@ decode_keys_json (const json_t *resp_obj,
           key_data->last_denom_issue_date
             = GNUNET_TIME_timestamp_max (key_data->last_denom_issue_date,
                                          dk.valid_from);
-        }; // json_array_foreach over denominations
+        }; /* end of json_array_foreach over denominations */
 
-        // The calculated group_hash_xor must be the same as group.hash from
-        // the json.
+        /* The calculated group_hash_xor must be the same as group.hash from
+           the JSON. */
         EXITIF (0 !=
-                GNUNET_CRYPTO_hash_cmp (&group_hash_xor, &group.hash));
+                GNUNET_CRYPTO_hash_cmp (&group_hash_xor,
+                                        &group.hash));
 
-      } // block for parsing individual denominations
-    }; // json_array_foreach over groups of denominations
+      } /* end of block for parsing individual denominations */
+    } /* end of json_array_foreach over groups of denominations */
   }
 
   /* parse the auditor information */
   {
-    json_t *auditors_array;
     json_t *auditor_info;
     unsigned int index;
 
-    EXITIF (NULL == (auditors_array =
-                       json_object_get (resp_obj,
-                                        "auditors")));
-    EXITIF (JSON_ARRAY != json_typeof (auditors_array));
-
     /* Merge with the existing auditor information we have (/keys cherry 
picking) */
-    json_array_foreach (auditors_array, index, auditor_info) {
+    json_array_foreach (auditors_array, index, auditor_info)
+    {
       struct TALER_EXCHANGE_AuditorInformation ai;
       bool found = false;
 
@@ -1105,41 +944,35 @@ decode_keys_json (const json_t *resp_obj,
   }
 
   /* parse the revocation/recoup information */
+  if (NULL != recoup_array)
   {
-    json_t *recoup_array;
     json_t *recoup_info;
     unsigned int index;
 
-    if (NULL != (recoup_array =
-                   json_object_get (resp_obj,
-                                    "recoup")))
+    json_array_foreach (recoup_array, index, recoup_info)
     {
-      EXITIF (JSON_ARRAY != json_typeof (recoup_array));
-
-      json_array_foreach (recoup_array, index, recoup_info) {
-        struct TALER_DenominationHashP h_denom_pub;
-        struct GNUNET_JSON_Specification spec[] = {
-          GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
-                                       &h_denom_pub),
-          GNUNET_JSON_spec_end ()
-        };
-
-        EXITIF (GNUNET_OK !=
-                GNUNET_JSON_parse (recoup_info,
-                                   spec,
-                                   NULL, NULL));
-        for (unsigned int j = 0;
-             j<key_data->num_denom_keys;
-             j++)
+      struct TALER_DenominationHashP h_denom_pub;
+      struct GNUNET_JSON_Specification spec[] = {
+        GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
+                                     &h_denom_pub),
+        GNUNET_JSON_spec_end ()
+      };
+
+      EXITIF (GNUNET_OK !=
+              GNUNET_JSON_parse (recoup_info,
+                                 spec,
+                                 NULL, NULL));
+      for (unsigned int j = 0;
+           j<key_data->num_denom_keys;
+           j++)
+      {
+        if (0 == GNUNET_memcmp (&h_denom_pub,
+                                &key_data->denom_keys[j].h_key))
         {
-          if (0 == GNUNET_memcmp (&h_denom_pub,
-                                  &key_data->denom_keys[j].h_key))
-          {
-            key_data->denom_keys[j].revoked = GNUNET_YES;
-            break;
-          }
+          key_data->denom_keys[j].revoked = true;
+          break;
         }
-      };
+      }
     }
   }
 
@@ -1148,7 +981,6 @@ decode_keys_json (const json_t *resp_obj,
     EXITIF (GNUNET_OK !=
             TALER_EXCHANGE_test_signing_key (key_data,
                                              &pub));
-
     EXITIF (GNUNET_OK !=
             TALER_exchange_online_key_set_verify (
               key_data->list_issue_date,
@@ -1156,7 +988,6 @@ decode_keys_json (const json_t *resp_obj,
               &pub,
               &denominations_sig));
   }
-
   return GNUNET_OK;
 
 EXITIF_exit:
@@ -1165,101 +996,6 @@ EXITIF_exit:
 }
 
 
-/**
- * Free key data object.
- *
- * @param key_data data to free (pointer itself excluded)
- */
-static void
-free_key_data (struct TALER_EXCHANGE_Keys *key_data)
-{
-  GNUNET_array_grow (key_data->sign_keys,
-                     key_data->num_sign_keys,
-                     0);
-  for (unsigned int i = 0; i<key_data->num_denom_keys; i++)
-    TALER_denom_pub_free (&key_data->denom_keys[i].key);
-
-  GNUNET_array_grow (key_data->denom_keys,
-                     key_data->denom_keys_size,
-                     0);
-  for (unsigned int i = 0; i<key_data->num_auditors; i++)
-  {
-    GNUNET_array_grow (key_data->auditors[i].denom_keys,
-                       key_data->auditors[i].num_denom_keys,
-                       0);
-    GNUNET_free (key_data->auditors[i].auditor_url);
-  }
-  GNUNET_array_grow (key_data->auditors,
-                     key_data->auditors_size,
-                     0);
-  GNUNET_free (key_data->wallet_balance_limit_without_kyc);
-  GNUNET_free (key_data->version);
-  GNUNET_free (key_data->currency);
-  GNUNET_free (key_data->asset_type);
-  GNUNET_free (key_data->global_fees);
-}
-
-
-/**
- * Initiate download of /keys from the exchange.
- *
- * @param cls exchange where to download /keys from
- */
-static void
-request_keys (void *cls);
-
-
-void
-TALER_EXCHANGE_set_last_denom (struct TALER_EXCHANGE_Handle *exchange,
-                               struct GNUNET_TIME_Timestamp last_denom_new)
-{
-  TALER_LOG_DEBUG (
-    "Application explicitly set last denomination validity to %s\n",
-    GNUNET_TIME_timestamp2s (last_denom_new));
-  exchange->key_data.last_denom_issue_date = last_denom_new;
-}
-
-
-struct GNUNET_TIME_Timestamp
-TALER_EXCHANGE_check_keys_current (struct TALER_EXCHANGE_Handle *exchange,
-                                   enum TALER_EXCHANGE_CheckKeysFlags flags,
-                                   TALER_EXCHANGE_CertificationCallback cb,
-                                   void *cb_cls)
-{
-  bool force_download = 0 != (flags & TALER_EXCHANGE_CKF_FORCE_DOWNLOAD);
-  bool pull_all_keys = 0 != (flags & TALER_EXCHANGE_CKF_PULL_ALL_KEYS);
-
-  GNUNET_assert (NULL != exchange);
-
-  if ( (NULL != cb) &&
-       ( (exchange->cert_cb != cb) ||
-         (exchange->cert_cb_cls != cb_cls) ) )
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Changing target of exchange certification callback\n");
-    exchange->cert_cb = cb;
-    exchange->cert_cb_cls = cb_cls;
-  }
-  if (NULL != exchange->kr)
-    return GNUNET_TIME_UNIT_ZERO_TS;
-  if (pull_all_keys)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Forcing re-download of all exchange keys\n");
-    GNUNET_break (force_download);
-    exchange->state = MHS_INIT;
-  }
-  if ( (! force_download) &&
-       (GNUNET_TIME_absolute_is_future (
-          exchange->key_data_expiration.abs_time)) )
-    return exchange->key_data_expiration;
-  if (NULL == exchange->retry_task)
-    exchange->retry_task = GNUNET_SCHEDULER_add_now (&request_keys,
-                                                     exchange);
-  return GNUNET_TIME_UNIT_ZERO_TS;
-}
-
-
 /**
  * Callback used when downloading the reply to a /keys request
  * is complete.
@@ -1273,133 +1009,107 @@ keys_completed_cb (void *cls,
                    long response_code,
                    const void *resp_obj)
 {
-  struct KeysRequest *kr = cls;
-  struct TALER_EXCHANGE_Handle *exchange = kr->exchange;
-  struct TALER_EXCHANGE_Keys kd_old;
+  struct TALER_EXCHANGE_GetKeysHandle *gkh = cls;
   const json_t *j = resp_obj;
-  struct TALER_EXCHANGE_Keys kd;
+  struct TALER_EXCHANGE_Keys *kd = NULL;
   struct TALER_EXCHANGE_KeysResponse kresp = {
     .hr.reply = j,
     .hr.http_status = (unsigned int) response_code,
     .details.ok.compat = TALER_EXCHANGE_VC_PROTOCOL_ERROR,
   };
 
-  memset (&kd,
-          0,
-          sizeof (kd));
+  gkh->job = NULL;
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Received keys from URL `%s' with status %ld and expiration 
%s.\n",
-              kr->url,
+              gkh->url,
               response_code,
-              GNUNET_TIME_timestamp2s (kr->expire));
-  if (GNUNET_TIME_absolute_is_past (kr->expire.abs_time))
+              GNUNET_TIME_timestamp2s (gkh->expire));
+  if (GNUNET_TIME_absolute_is_past (gkh->expire.abs_time))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Exchange failed to give expiration time, assuming in %s\n",
                 GNUNET_TIME_relative2s (DEFAULT_EXPIRATION,
                                         true));
-    kr->expire
+    gkh->expire
       = GNUNET_TIME_absolute_to_timestamp (
           GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION));
   }
-  kd_old = exchange->key_data;
   switch (response_code)
   {
   case 0:
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Failed to receive /keys response from exchange %s\n",
-                exchange->url);
-    free_keys_request (kr);
-    exchange->keys_error_count++;
-    exchange->kr = NULL;
-    GNUNET_assert (NULL == exchange->retry_task);
-    exchange->retry_delay = EXCHANGE_LIB_BACKOFF (exchange->retry_delay);
-    exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay,
-                                                         &request_keys,
-                                                         exchange);
-    return;
+                gkh->exchange_url);
+    break;
   case MHD_HTTP_OK:
-    exchange->keys_error_count = 0;
     if (NULL == j)
     {
+      GNUNET_break (0);
       response_code = 0;
       break;
     }
-    /* We keep the denomination keys and auditor signatures from the
-       previous iteration (/keys cherry picking) */
-    kd.num_denom_keys
-      = kd_old.num_denom_keys;
-    kd.last_denom_issue_date
-      = kd_old.last_denom_issue_date;
-    GNUNET_array_grow (kd.denom_keys,
-                       kd.denom_keys_size,
-                       kd.num_denom_keys);
-
-    /* First make a shallow copy, we then need another pass for the RSA key... 
*/
-    GNUNET_memcpy (kd.denom_keys,
-                   kd_old.denom_keys,
-                   kd_old.num_denom_keys * sizeof (struct
-                                                   
TALER_EXCHANGE_DenomPublicKey));
-
-    for (unsigned int i = 0; i<kd_old.num_denom_keys; i++)
-      TALER_denom_pub_deep_copy (&kd.denom_keys[i].key,
-                                 &kd_old.denom_keys[i].key);
-
-    kd.num_auditors = kd_old.num_auditors;
-    kd.auditors = GNUNET_new_array (kd.num_auditors,
-                                    struct TALER_EXCHANGE_AuditorInformation);
-    /* Now the necessary deep copy... */
-    for (unsigned int i = 0; i<kd_old.num_auditors; i++)
+    kd = GNUNET_new (struct TALER_EXCHANGE_Keys);
+    kd->exchange_url = GNUNET_strdup (gkh->exchange_url);
+    if (NULL != gkh->prev_keys)
     {
-      const struct TALER_EXCHANGE_AuditorInformation *aold =
-        &kd_old.auditors[i];
-      struct TALER_EXCHANGE_AuditorInformation *anew = &kd.auditors[i];
-
-      anew->auditor_pub = aold->auditor_pub;
-      GNUNET_assert (NULL != aold->auditor_url);
-      anew->auditor_url = GNUNET_strdup (aold->auditor_url);
-      GNUNET_array_grow (anew->denom_keys,
-                         anew->num_denom_keys,
-                         aold->num_denom_keys);
-      GNUNET_memcpy (anew->denom_keys,
-                     aold->denom_keys,
-                     aold->num_denom_keys
-                     * sizeof (struct TALER_EXCHANGE_AuditorDenominationInfo));
-    }
+      const struct TALER_EXCHANGE_Keys *kd_old = gkh->prev_keys;
+
+      /* We keep the denomination keys and auditor signatures from the
+         previous iteration (/keys cherry picking) */
+      kd->num_denom_keys
+        = kd_old->num_denom_keys;
+      kd->last_denom_issue_date
+        = kd_old->last_denom_issue_date;
+      GNUNET_array_grow (kd->denom_keys,
+                         kd->denom_keys_size,
+                         kd->num_denom_keys);
+      /* First make a shallow copy, we then need another pass for the RSA 
key... */
+      GNUNET_memcpy (kd->denom_keys,
+                     kd_old->denom_keys,
+                     kd_old->num_denom_keys
+                     * sizeof (struct TALER_EXCHANGE_DenomPublicKey));
+      for (unsigned int i = 0; i<kd_old->num_denom_keys; i++)
+        TALER_denom_pub_deep_copy (&kd->denom_keys[i].key,
+                                   &kd_old->denom_keys[i].key);
+      kd->num_auditors = kd_old->num_auditors;
+      kd->auditors = GNUNET_new_array (kd->num_auditors,
+                                       struct 
TALER_EXCHANGE_AuditorInformation);
+      /* Now the necessary deep copy... */
+      for (unsigned int i = 0; i<kd_old->num_auditors; i++)
+      {
+        const struct TALER_EXCHANGE_AuditorInformation *aold =
+          &kd_old->auditors[i];
+        struct TALER_EXCHANGE_AuditorInformation *anew = &kd->auditors[i];
 
-    /* Old auditors got just copied into new ones.  */
+        anew->auditor_pub = aold->auditor_pub;
+        anew->auditor_url = GNUNET_strdup (aold->auditor_url);
+        GNUNET_array_grow (anew->denom_keys,
+                           anew->num_denom_keys,
+                           aold->num_denom_keys);
+        GNUNET_memcpy (
+          anew->denom_keys,
+          aold->denom_keys,
+          aold->num_denom_keys
+          * sizeof (struct TALER_EXCHANGE_AuditorDenominationInfo));
+      }
+    }
+    /* Now decode fresh /keys response */
     if (GNUNET_OK !=
         decode_keys_json (j,
                           true,
-                          &kd,
+                          kd,
                           &kresp.details.ok.compat))
     {
       TALER_LOG_ERROR ("Could not decode /keys response\n");
+      kd->rc = 1;
+      TALER_EXCHANGE_keys_decref (kd);
+      kd = NULL;
       kresp.hr.http_status = 0;
       kresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-      for (unsigned int i = 0; i<kd.num_auditors; i++)
-      {
-        struct TALER_EXCHANGE_AuditorInformation *anew = &kd.auditors[i];
-
-        GNUNET_array_grow (anew->denom_keys,
-                           anew->num_denom_keys,
-                           0);
-        GNUNET_free (anew->auditor_url);
-      }
-      GNUNET_free (kd.auditors);
-      kd.auditors = NULL;
-      kd.num_auditors = 0;
-      for (unsigned int i = 0; i<kd_old.num_denom_keys; i++)
-        TALER_denom_pub_free (&kd.denom_keys[i].key);
-      GNUNET_array_grow (kd.denom_keys,
-                         kd.denom_keys_size,
-                         0);
-      kd.num_denom_keys = 0;
       break;
     }
-    json_decref (exchange->key_data_raw);
-    exchange->key_data_raw = json_deep_copy (j);
-    exchange->retry_delay = GNUNET_TIME_UNIT_ZERO;
+    kd->rc = 1;
+    kresp.details.ok.keys = kd;
     break;
   case MHD_HTTP_BAD_REQUEST:
   case MHD_HTTP_UNAUTHORIZED:
@@ -1417,8 +1127,6 @@ keys_completed_cb (void *cls,
     }
     break;
   default:
-    if (MHD_HTTP_GATEWAY_TIMEOUT == response_code)
-      exchange->keys_error_count++;
     if (NULL == j)
     {
       kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
@@ -1435,78 +1143,10 @@ keys_completed_cb (void *cls,
                 (int) kresp.hr.ec);
     break;
   }
-  exchange->key_data = kd;
-  if (GNUNET_TIME_absolute_is_past (
-        exchange->key_data.last_denom_issue_date.abs_time))
-    TALER_LOG_WARNING ("Last DK issue date from exchange is in the past: %s\n",
-                       GNUNET_TIME_timestamp2s (
-                         exchange->key_data.last_denom_issue_date));
-  else
-    TALER_LOG_DEBUG ("Last DK issue date updated to: %s\n",
-                     GNUNET_TIME_timestamp2s (
-                       exchange->key_data.last_denom_issue_date));
-
-
-  if (MHD_HTTP_OK != response_code)
-  {
-    exchange->kr = NULL;
-    free_keys_request (kr);
-    exchange->state = MHS_FAILED;
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Exchange keys download failed\n");
-    if (NULL != exchange->key_data_raw)
-    {
-      json_decref (exchange->key_data_raw);
-      exchange->key_data_raw = NULL;
-    }
-    free_key_data (&kd_old);
-    /* notify application that we failed */
-    exchange->cert_cb (exchange->cert_cb_cls,
-                       &kresp);
-    return;
-  }
-
-  exchange->kr = NULL;
-  exchange->key_data_expiration = kr->expire;
-  free_keys_request (kr);
-  exchange->state = MHS_CERT;
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Successfully downloaded exchange's keys\n");
-  update_auditors (exchange);
-  kresp.details.ok.keys = &exchange->key_data;
-
-  /* notify application about the key information */
-  exchange->cert_cb (exchange->cert_cb_cls,
-                     &kresp);
-  free_key_data (&kd_old);
-}
-
-
-/* ********************* library internal API ********* */
-
-
-struct GNUNET_CURL_Context *
-TEAH_handle_to_context (struct TALER_EXCHANGE_Handle *h)
-{
-  return h->ctx;
-}
-
-
-enum GNUNET_GenericReturnValue
-TEAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h)
-{
-  return (MHS_CERT == h->state) ? GNUNET_YES : GNUNET_NO;
-}
-
-
-char *
-TEAH_path_to_url (struct TALER_EXCHANGE_Handle *h,
-                  const char *path)
-{
-  GNUNET_assert ('/' == path[0]);
-  return TALER_url_join (h->url,
-                         path + 1,
-                         NULL);
+  gkh->cert_cb (gkh->cert_cb_cls,
+                &kresp,
+                kd);
+  TALER_EXCHANGE_get_keys_cancel (gkh);
 }
 
 
@@ -1520,7 +1160,7 @@ TEAH_path_to_url (struct TALER_EXCHANGE_Handle *h,
  * Parse HTTP timestamp.
  *
  * @param dateline header to parse header
- * @param at where to write the result
+ * @param[out] at where to write the result
  * @return #GNUNET_OK on success
  */
 static enum GNUNET_GenericReturnValue
@@ -1620,7 +1260,7 @@ parse_date_string (const char *dateline,
  * @param buffer header data received
  * @param size size of an item in @a buffer
  * @param nitems number of items in @a buffer
- * @param userdata the `struct KeysRequest`
+ * @param userdata the `struct TALER_EXCHANGE_GetKeysHandle`
  * @return `size * nitems` on success (everything else aborts)
  */
 static size_t
@@ -1629,7 +1269,7 @@ header_cb (char *buffer,
            size_t nitems,
            void *userdata)
 {
-  struct KeysRequest *kr = userdata;
+  struct TALER_EXCHANGE_GetKeysHandle *kr = userdata;
   size_t total = size * nitems;
   char *val;
 
@@ -1656,22 +1296,249 @@ header_cb (char *buffer,
 }
 
 
-/* ********************* public API ******************* */
+struct TALER_EXCHANGE_GetKeysHandle *
+TALER_EXCHANGE_get_keys (
+  struct GNUNET_CURL_Context *ctx,
+  const char *url,
+  struct TALER_EXCHANGE_Keys *last_keys,
+  TALER_EXCHANGE_GetKeysCallback cert_cb,
+  void *cert_cb_cls)
+{
+  struct TALER_EXCHANGE_GetKeysHandle *gkh;
+  CURL *eh;
+  char last_date[80] = { 0 };
 
+  TALER_LOG_DEBUG ("Connecting to the exchange (%s)\n",
+                   url);
+  gkh = GNUNET_new (struct TALER_EXCHANGE_GetKeysHandle);
+  gkh->exchange_url = GNUNET_strdup (url);
+  gkh->cert_cb = cert_cb;
+  gkh->cert_cb_cls = cert_cb_cls;
+  if (NULL != last_keys)
+  {
+    TALER_LOG_DEBUG ("Last DK issue date (before GETting /keys): %s\n",
+                     GNUNET_TIME_timestamp2s (
+                       last_keys->last_denom_issue_date));
+    GNUNET_snprintf (last_date,
+                     sizeof (last_date),
+                     "%llu",
+                     (unsigned long long)
+                     last_keys->last_denom_issue_date.abs_time.abs_value_us
+                     / 1000000LLU);
+  }
+  gkh->url = TALER_url_join (url,
+                             "keys",
+                             (NULL != last_keys)
+                             ? "last_issue_date"
+                             : NULL,
+                             (NULL != last_keys)
+                             ? last_date
+                             : NULL,
+                             NULL);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Requesting keys with URL `%s'.\n",
+              gkh->url);
+  eh = TALER_EXCHANGE_curl_easy_get_ (gkh->url);
+  if (NULL == eh)
+  {
+    GNUNET_break (0);
+    GNUNET_free (gkh->exchange_url);
+    GNUNET_free (gkh->url);
+    GNUNET_free (gkh);
+    return NULL;
+  }
+  GNUNET_break (CURLE_OK ==
+                curl_easy_setopt (eh,
+                                  CURLOPT_VERBOSE,
+                                  0));
+  GNUNET_break (CURLE_OK ==
+                curl_easy_setopt (eh,
+                                  CURLOPT_TIMEOUT,
+                                  120 /* seconds */));
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_HEADERFUNCTION,
+                                   &header_cb));
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_HEADERDATA,
+                                   gkh));
+  gkh->job = GNUNET_CURL_job_add_with_ct_json (ctx,
+                                               eh,
+                                               &keys_completed_cb,
+                                               gkh);
+  return gkh;
+}
 
-/**
- * Deserialize the key data and use it to bootstrap @a exchange to
- * more efficiently recover the state.  Errors in @a data must be
- * tolerated (i.e. by re-downloading instead).
- *
- * @param exchange which exchange's key and wire data should be deserialized
- * @param data the data to deserialize
- */
-static void
-deserialize_data (struct TALER_EXCHANGE_Handle *exchange,
-                  const json_t *data)
+
+void
+TALER_EXCHANGE_get_keys_cancel (
+  struct TALER_EXCHANGE_GetKeysHandle *gkh)
 {
-  const json_t *keys;
+  if (NULL != gkh->job)
+  {
+    GNUNET_CURL_job_cancel (gkh->job);
+    gkh->job = NULL;
+  }
+  TALER_EXCHANGE_keys_decref (gkh->prev_keys);
+  GNUNET_free (gkh->exchange_url);
+  GNUNET_free (gkh->url);
+  GNUNET_free (gkh);
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_test_signing_key (
+  const struct TALER_EXCHANGE_Keys *keys,
+  const struct TALER_ExchangePublicKeyP *pub)
+{
+  struct GNUNET_TIME_Absolute now;
+
+  /* we will check using a tolerance of 1h for the time */
+  now = GNUNET_TIME_absolute_get ();
+  for (unsigned int i = 0; i<keys->num_sign_keys; i++)
+    if ( (GNUNET_TIME_absolute_cmp (
+            keys->sign_keys[i].valid_from.abs_time,
+            <=,
+            GNUNET_TIME_absolute_add (now,
+                                      LIFETIME_TOLERANCE))) &&
+         (GNUNET_TIME_absolute_cmp (
+            keys->sign_keys[i].valid_until.abs_time,
+            >,
+            GNUNET_TIME_absolute_subtract (now,
+                                           LIFETIME_TOLERANCE))) &&
+         (0 == GNUNET_memcmp (pub,
+                              &keys->sign_keys[i].key)) )
+      return GNUNET_OK;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Signing key not valid at time %s\n",
+              GNUNET_TIME_absolute2s (now));
+  return GNUNET_SYSERR;
+}
+
+
+const struct TALER_EXCHANGE_DenomPublicKey *
+TALER_EXCHANGE_get_denomination_key (
+  const struct TALER_EXCHANGE_Keys *keys,
+  const struct TALER_DenominationPublicKey *pk)
+{
+  for (unsigned int i = 0; i<keys->num_denom_keys; i++)
+    if (0 ==
+        TALER_denom_pub_cmp (pk,
+                             &keys->denom_keys[i].key))
+      return &keys->denom_keys[i];
+  return NULL;
+}
+
+
+const struct TALER_EXCHANGE_GlobalFee *
+TALER_EXCHANGE_get_global_fee (
+  const struct TALER_EXCHANGE_Keys *keys,
+  struct GNUNET_TIME_Timestamp ts)
+{
+  for (unsigned int i = 0; i<keys->num_global_fees; i++)
+  {
+    const struct TALER_EXCHANGE_GlobalFee *gf = &keys->global_fees[i];
+
+    if (GNUNET_TIME_timestamp_cmp (ts,
+                                   >=,
+                                   gf->start_date) &&
+        GNUNET_TIME_timestamp_cmp (ts,
+                                   <,
+                                   gf->end_date))
+      return gf;
+  }
+  return NULL;
+}
+
+
+struct TALER_EXCHANGE_DenomPublicKey *
+TALER_EXCHANGE_copy_denomination_key (
+  const struct TALER_EXCHANGE_DenomPublicKey *key)
+{
+  struct TALER_EXCHANGE_DenomPublicKey *copy;
+
+  copy = GNUNET_new (struct TALER_EXCHANGE_DenomPublicKey);
+  *copy = *key;
+  TALER_denom_pub_deep_copy (&copy->key,
+                             &key->key);
+  return copy;
+}
+
+
+void
+TALER_EXCHANGE_destroy_denomination_key (
+  struct TALER_EXCHANGE_DenomPublicKey *key)
+{
+  TALER_denom_pub_free (&key->key);
+  GNUNET_free (key);
+}
+
+
+const struct TALER_EXCHANGE_DenomPublicKey *
+TALER_EXCHANGE_get_denomination_key_by_hash (
+  const struct TALER_EXCHANGE_Keys *keys,
+  const struct TALER_DenominationHashP *hc)
+{
+  for (unsigned int i = 0; i<keys->num_denom_keys; i++)
+    if (0 == GNUNET_memcmp (hc,
+                            &keys->denom_keys[i].h_key))
+      return &keys->denom_keys[i];
+  return NULL;
+}
+
+
+struct TALER_EXCHANGE_Keys *
+TALER_EXCHANGE_keys_incref (struct TALER_EXCHANGE_Keys *keys)
+{
+  GNUNET_assert (keys->rc < UINT_MAX);
+  keys->rc++;
+  return keys;
+}
+
+
+void
+TALER_EXCHANGE_keys_decref (struct TALER_EXCHANGE_Keys *keys)
+{
+  if (NULL == keys)
+    return;
+  GNUNET_assert (0 < keys->rc);
+  keys->rc--;
+  if (0 != keys->rc)
+    return;
+  GNUNET_array_grow (keys->sign_keys,
+                     keys->num_sign_keys,
+                     0);
+  for (unsigned int i = 0; i<keys->num_denom_keys; i++)
+    TALER_denom_pub_free (&keys->denom_keys[i].key);
+
+  GNUNET_array_grow (keys->denom_keys,
+                     keys->denom_keys_size,
+                     0);
+  for (unsigned int i = 0; i<keys->num_auditors; i++)
+  {
+    GNUNET_array_grow (keys->auditors[i].denom_keys,
+                       keys->auditors[i].num_denom_keys,
+                       0);
+    GNUNET_free (keys->auditors[i].auditor_url);
+  }
+  GNUNET_array_grow (keys->auditors,
+                     keys->auditors_size,
+                     0);
+  GNUNET_free (keys->wallet_balance_limit_without_kyc);
+  GNUNET_free (keys->version);
+  GNUNET_free (keys->currency);
+  GNUNET_free (keys->asset_type);
+  GNUNET_free (keys->global_fees);
+  GNUNET_free (keys->exchange_url);
+  GNUNET_free (keys);
+}
+
+
+struct TALER_EXCHANGE_Keys *
+TALER_EXCHANGE_keys_from_json (const json_t *j)
+{
+  const json_t *jkeys;
   const char *url;
   uint32_t version;
   struct GNUNET_TIME_Timestamp expire;
@@ -1679,72 +1546,50 @@ deserialize_data (struct TALER_EXCHANGE_Handle 
*exchange,
     GNUNET_JSON_spec_uint32 ("version",
                              &version),
     GNUNET_JSON_spec_array_const ("keys",
-                                  &keys),
+                                  &jkeys),
     GNUNET_JSON_spec_string ("exchange_url",
                              &url),
     GNUNET_JSON_spec_timestamp ("expire",
                                 &expire),
     GNUNET_JSON_spec_end ()
   };
-  struct TALER_EXCHANGE_Keys key_data;
-  struct TALER_EXCHANGE_KeysResponse kresp = {
-    .hr.ec = TALER_EC_NONE,
-    .hr.http_status = MHD_HTTP_OK,
-    .hr.reply = data,
-    .details.ok.keys = &exchange->key_data
-  };
+  struct TALER_EXCHANGE_Keys *keys;
+  enum TALER_EXCHANGE_VersionCompatibility compat;
 
-  if (NULL == data)
-    return;
+  if (NULL == j)
+    return NULL;
   if (GNUNET_OK !=
-      GNUNET_JSON_parse (data,
+      GNUNET_JSON_parse (j,
                          spec,
                          NULL, NULL))
   {
     GNUNET_break_op (0);
-    return;
+    return NULL;
   }
   if (0 != version)
   {
-    return; /* unsupported version */
-  }
-  if (0 != strcmp (url,
-                   exchange->url))
-  {
-    GNUNET_break (0);
-    return;
+    return NULL; /* unsupported version */
   }
-  memset (&key_data,
-          0,
-          sizeof (struct TALER_EXCHANGE_Keys));
+  keys = GNUNET_new (struct TALER_EXCHANGE_Keys);
   if (GNUNET_OK !=
-      decode_keys_json (keys,
+      decode_keys_json (jkeys,
                         false,
-                        &key_data,
-                        &kresp.details.ok.compat))
+                        keys,
+                        &compat))
   {
     GNUNET_break (0);
-    return;
+    return NULL;
   }
-  /* decode successful, initialize with the result */
-  GNUNET_assert (NULL == exchange->key_data_raw);
-  exchange->key_data_raw = json_deep_copy (keys);
-  exchange->key_data = key_data;
-  exchange->key_data_expiration = expire;
-  exchange->state = MHS_CERT;
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Successfully loaded exchange's keys via deserialization\n");
-  update_auditors (exchange);
-  /* notify application about the key information */
-  exchange->cert_cb (exchange->cert_cb_cls,
-                     &kresp);
+  keys->rc = 1;
+  keys->key_data_expiration = expire;
+  keys->exchange_url = GNUNET_strdup (url);
+  return keys;
 }
 
 
 json_t *
-TALER_EXCHANGE_serialize_data (struct TALER_EXCHANGE_Handle *exchange)
+TALER_EXCHANGE_keys_to_json (const struct TALER_EXCHANGE_Keys *kd)
 {
-  const struct TALER_EXCHANGE_Keys *kd = &exchange->key_data;
   struct GNUNET_TIME_Timestamp now;
   json_t *keys;
   json_t *signkeys;
@@ -1903,371 +1748,12 @@ TALER_EXCHANGE_serialize_data (struct 
TALER_EXCHANGE_Handle *exchange)
     GNUNET_JSON_pack_uint64 ("version",
                              EXCHANGE_SERIALIZATION_FORMAT_VERSION),
     GNUNET_JSON_pack_timestamp ("expire",
-                                exchange->key_data_expiration),
+                                kd->key_data_expiration),
     GNUNET_JSON_pack_string ("exchange_url",
-                             exchange->url),
+                             kd->exchange_url),
     GNUNET_JSON_pack_object_steal ("keys",
                                    keys));
 }
 
 
-struct TALER_EXCHANGE_Handle *
-TALER_EXCHANGE_connect (
-  struct GNUNET_CURL_Context *ctx,
-  const char *url,
-  TALER_EXCHANGE_CertificationCallback cert_cb,
-  void *cert_cb_cls,
-  ...)
-{
-  struct TALER_EXCHANGE_Handle *exchange;
-  va_list ap;
-  enum TALER_EXCHANGE_Option opt;
-
-  TALER_LOG_DEBUG ("Connecting to the exchange (%s)\n",
-                   url);
-  /* Disable 100 continue processing */
-  GNUNET_break (GNUNET_OK ==
-                GNUNET_CURL_append_header (ctx,
-                                           MHD_HTTP_HEADER_EXPECT ":"));
-  exchange = GNUNET_new (struct TALER_EXCHANGE_Handle);
-  exchange->ctx = ctx;
-  exchange->url = GNUNET_strdup (url);
-  exchange->cert_cb = cert_cb;
-  exchange->cert_cb_cls = cert_cb_cls;
-  exchange->retry_task = GNUNET_SCHEDULER_add_now (&request_keys,
-                                                   exchange);
-  va_start (ap, cert_cb_cls);
-  while (TALER_EXCHANGE_OPTION_END !=
-         (opt = va_arg (ap, int)))
-  {
-    switch (opt)
-    {
-    case TALER_EXCHANGE_OPTION_END:
-      GNUNET_assert (0);
-      break;
-    case TALER_EXCHANGE_OPTION_DATA:
-      {
-        const json_t *data = va_arg (ap, const json_t *);
-
-        deserialize_data (exchange,
-                          data);
-        break;
-      }
-    default:
-      GNUNET_assert (0);
-      break;
-    }
-  }
-  va_end (ap);
-  return exchange;
-}
-
-
-/**
- * Compute the network timeout for the next request to /keys.
- *
- * @param exchange the exchange handle
- * @returns the timeout in seconds (for use by CURL)
- */
-static long
-get_keys_timeout_seconds (struct TALER_EXCHANGE_Handle *exchange)
-{
-  unsigned int kec;
-
-  /* if retry counter >= 8, do not bother to go further, we
-     stop the exponential back-off at 128 anyway. */
-  kec = GNUNET_MIN (7,
-                    exchange->keys_error_count);
-  return GNUNET_MIN (120,
-                     5 + (1L << kec));
-}
-
-
-/**
- * Initiate download of /keys from the exchange.
- *
- * @param cls exchange where to download /keys from
- */
-static void
-request_keys (void *cls)
-{
-  struct TALER_EXCHANGE_Handle *exchange = cls;
-  struct KeysRequest *kr;
-  CURL *eh;
-  char url[200] = "/keys?";
-
-  exchange->retry_task = NULL;
-  GNUNET_assert (NULL == exchange->kr);
-  kr = GNUNET_new (struct KeysRequest);
-  kr->exchange = exchange;
-
-  if (GNUNET_YES == TEAH_handle_is_ready (exchange))
-  {
-    TALER_LOG_DEBUG ("Last DK issue date (before GETting /keys): %s\n",
-                     GNUNET_TIME_timestamp2s (
-                       exchange->key_data.last_denom_issue_date));
-    sprintf (&url[strlen (url)],
-             "last_issue_date=%llu&",
-             (unsigned long long)
-             exchange->key_data.last_denom_issue_date.abs_time.abs_value_us
-             / 1000000LLU);
-  }
-
-  /* Clean the last '&'/'?' sign that we optimistically put.  */
-  url[strlen (url) - 1] = '\0';
-  kr->url = TEAH_path_to_url (exchange,
-                              url);
-  if (NULL == kr->url)
-  {
-    struct TALER_EXCHANGE_KeysResponse kresp = {
-      .hr.ec = TALER_EC_GENERIC_CONFIGURATION_INVALID,
-      /* Next line is technically unnecessary, as the
-         http status we set is 0 */
-      .details.ok.compat = TALER_EXCHANGE_VC_PROTOCOL_ERROR
-    };
-
-    GNUNET_free (kr);
-    exchange->keys_error_count++;
-    exchange->state = MHS_FAILED;
-    exchange->cert_cb (exchange->cert_cb_cls,
-                       &kresp);
-    return;
-  }
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Requesting keys with URL `%s'.\n",
-              kr->url);
-  eh = TALER_EXCHANGE_curl_easy_get_ (kr->url);
-  if (NULL == eh)
-  {
-    GNUNET_free (kr->url);
-    GNUNET_free (kr);
-    exchange->retry_delay = EXCHANGE_LIB_BACKOFF (exchange->retry_delay);
-    exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay,
-                                                         &request_keys,
-                                                         exchange);
-    return;
-  }
-  GNUNET_break (CURLE_OK ==
-                curl_easy_setopt (eh,
-                                  CURLOPT_VERBOSE,
-                                  0));
-  GNUNET_break (CURLE_OK ==
-                curl_easy_setopt (eh,
-                                  CURLOPT_TIMEOUT,
-                                  get_keys_timeout_seconds (exchange)));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_HEADERFUNCTION,
-                                   &header_cb));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_HEADERDATA,
-                                   kr));
-  kr->job = GNUNET_CURL_job_add_with_ct_json (exchange->ctx,
-                                              eh,
-                                              &keys_completed_cb,
-                                              kr);
-  exchange->kr = kr;
-}
-
-
-void
-TALER_EXCHANGE_disconnect (struct TALER_EXCHANGE_Handle *exchange)
-{
-  struct TEAH_AuditorListEntry *ale;
-
-  while (NULL != (ale = exchange->auditors_head))
-  {
-    GNUNET_CONTAINER_DLL_remove (exchange->auditors_head,
-                                 exchange->auditors_tail,
-                                 ale);
-    if (NULL != ale->ah)
-    {
-      TALER_AUDITOR_get_config_cancel (ale->ah);
-      ale->ah = NULL;
-    }
-    GNUNET_free (ale->auditor_url);
-    GNUNET_free (ale);
-  }
-  if (NULL != exchange->kr)
-  {
-    GNUNET_CURL_job_cancel (exchange->kr->job);
-    free_keys_request (exchange->kr);
-    exchange->kr = NULL;
-  }
-  free_key_data (&exchange->key_data);
-  if (NULL != exchange->key_data_raw)
-  {
-    json_decref (exchange->key_data_raw);
-    exchange->key_data_raw = NULL;
-  }
-  if (NULL != exchange->retry_task)
-  {
-    GNUNET_SCHEDULER_cancel (exchange->retry_task);
-    exchange->retry_task = NULL;
-  }
-  GNUNET_free (exchange->url);
-  GNUNET_free (exchange);
-}
-
-
-enum GNUNET_GenericReturnValue
-TALER_EXCHANGE_test_signing_key (const struct TALER_EXCHANGE_Keys *keys,
-                                 const struct TALER_ExchangePublicKeyP *pub)
-{
-  struct GNUNET_TIME_Absolute now;
-
-  /* we will check using a tolerance of 1h for the time */
-  now = GNUNET_TIME_absolute_get ();
-  for (unsigned int i = 0; i<keys->num_sign_keys; i++)
-    if ( (GNUNET_TIME_absolute_cmp (
-            keys->sign_keys[i].valid_from.abs_time,
-            <=,
-            GNUNET_TIME_absolute_add (now,
-                                      LIFETIME_TOLERANCE))) &&
-         (GNUNET_TIME_absolute_cmp (
-            keys->sign_keys[i].valid_until.abs_time,
-            >,
-            GNUNET_TIME_absolute_subtract (now,
-                                           LIFETIME_TOLERANCE))) &&
-         (0 == GNUNET_memcmp (pub,
-                              &keys->sign_keys[i].key)) )
-      return GNUNET_OK;
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-              "Signing key not valid at time %s\n",
-              GNUNET_TIME_absolute2s (now));
-  return GNUNET_SYSERR;
-}
-
-
-const char *
-TALER_EXCHANGE_get_base_url (const struct TALER_EXCHANGE_Handle *exchange)
-{
-  return exchange->url;
-}
-
-
-const struct TALER_EXCHANGE_DenomPublicKey *
-TALER_EXCHANGE_get_denomination_key (
-  const struct TALER_EXCHANGE_Keys *keys,
-  const struct TALER_DenominationPublicKey *pk)
-{
-  for (unsigned int i = 0; i<keys->num_denom_keys; i++)
-    if (0 ==
-        TALER_denom_pub_cmp (pk,
-                             &keys->denom_keys[i].key))
-      return &keys->denom_keys[i];
-  return NULL;
-}
-
-
-const struct TALER_EXCHANGE_GlobalFee *
-TALER_EXCHANGE_get_global_fee (
-  const struct TALER_EXCHANGE_Keys *keys,
-  struct GNUNET_TIME_Timestamp ts)
-{
-  for (unsigned int i = 0; i<keys->num_global_fees; i++)
-  {
-    const struct TALER_EXCHANGE_GlobalFee *gf = &keys->global_fees[i];
-
-    if (GNUNET_TIME_timestamp_cmp (ts,
-                                   >=,
-                                   gf->start_date) &&
-        GNUNET_TIME_timestamp_cmp (ts,
-                                   <,
-                                   gf->end_date))
-      return gf;
-  }
-  return NULL;
-}
-
-
-struct TALER_EXCHANGE_DenomPublicKey *
-TALER_EXCHANGE_copy_denomination_key (
-  const struct TALER_EXCHANGE_DenomPublicKey *key)
-{
-  struct TALER_EXCHANGE_DenomPublicKey *copy;
-
-  copy = GNUNET_new (struct TALER_EXCHANGE_DenomPublicKey);
-  *copy = *key;
-  TALER_denom_pub_deep_copy (&copy->key,
-                             &key->key);
-  return copy;
-}
-
-
-void
-TALER_EXCHANGE_destroy_denomination_key (
-  struct TALER_EXCHANGE_DenomPublicKey *key)
-{
-  TALER_denom_pub_free (&key->key);
-  GNUNET_free (key);
-}
-
-
-const struct TALER_EXCHANGE_DenomPublicKey *
-TALER_EXCHANGE_get_denomination_key_by_hash (
-  const struct TALER_EXCHANGE_Keys *keys,
-  const struct TALER_DenominationHashP *hc)
-{
-  for (unsigned int i = 0; i<keys->num_denom_keys; i++)
-    if (0 == GNUNET_memcmp (hc,
-                            &keys->denom_keys[i].h_key))
-      return &keys->denom_keys[i];
-  return NULL;
-}
-
-
-struct TALER_EXCHANGE_Keys *
-TALER_EXCHANGE_get_keys (struct TALER_EXCHANGE_Handle *exchange)
-{
-  (void) TALER_EXCHANGE_check_keys_current (exchange,
-                                            TALER_EXCHANGE_CKF_NONE,
-                                            NULL,
-                                            NULL);
-  return &exchange->key_data;
-}
-
-
-json_t *
-TALER_EXCHANGE_get_keys_raw (struct TALER_EXCHANGE_Handle *exchange)
-{
-  (void) TALER_EXCHANGE_check_keys_current (exchange,
-                                            TALER_EXCHANGE_CKF_NONE,
-                                            NULL,
-                                            NULL);
-  return json_deep_copy (exchange->key_data_raw);
-}
-
-
-/**
- * Obtain the keys from the exchange in the raw JSON format.
- *
- * @param keys the keys structure
- * @return the keys in raw JSON
- */
-json_t *
-TALER_EXCHANGE_keys_to_json (struct TALER_EXCHANGE_Keys *keys)
-{
-  // FIXME!
-  return NULL;
-}
-
-
-struct TALER_EXCHANGE_Keys *
-TALER_EXCHANGE_keys_incref (struct TALER_EXCHANGE_Keys *keys)
-{
-  // FIXME
-  return keys;
-}
-
-
-void
-TALER_EXCHANGE_keys_decref (struct TALER_EXCHANGE_Keys *keys)
-{
-  // FIXME
-}
-
-
 /* end of exchange_api_handle.c */
diff --git a/src/lib/exchange_api_handle.h b/src/lib/exchange_api_handle.h
index 6b96e21e..7c01b9a9 100644
--- a/src/lib/exchange_api_handle.h
+++ b/src/lib/exchange_api_handle.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015 Taler Systems SA
+  Copyright (C) 2014, 2015, 2023 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
@@ -28,124 +28,6 @@
 #include "taler_util.h"
 #include "taler_curl_lib.h"
 
-/**
- * Entry in DLL of auditors used by an exchange.
- */
-struct TEAH_AuditorListEntry;
-
-
-/**
- * Stages of initialization for the `struct TALER_EXCHANGE_Handle`
- */
-enum ExchangeHandleState
-{
-  /**
-   * Just allocated.
-   */
-  MHS_INIT = 0,
-
-  /**
-   * Obtained the exchange's certification data and keys.
-   */
-  MHS_CERT = 1,
-
-  /**
-   * Failed to initialize (fatal).
-   */
-  MHS_FAILED = 2
-};
-
-
-/**
- * Handle to the exchange
- */
-struct TALER_EXCHANGE_Handle
-{
-  /**
-   * The context of this handle
-   */
-  struct GNUNET_CURL_Context *ctx;
-
-  /**
-   * The URL of the exchange (i.e. "http://exchange.taler.net/";)
-   */
-  char *url;
-
-  /**
-   * Function to call with the exchange's certification data,
-   * NULL if this has already been done.
-   */
-  TALER_EXCHANGE_CertificationCallback cert_cb;
-
-  /**
-   * Closure to pass to @e cert_cb.
-   */
-  void *cert_cb_cls;
-
-  /**
-   * Data for the request to get the /keys of a exchange,
-   * NULL once we are past stage #MHS_INIT.
-   */
-  struct KeysRequest *kr;
-
-  /**
-   * Task for retrying /keys request.
-   */
-  struct GNUNET_SCHEDULER_Task *retry_task;
-
-  /**
-   * Raw key data of the exchange, only valid if
-   * @e handshake_complete is past stage #MHS_CERT.
-   */
-  json_t *key_data_raw;
-
-  /**
-   * Head of DLL of auditors of this exchange.
-   */
-  struct TEAH_AuditorListEntry *auditors_head;
-
-  /**
-   * Tail of DLL of auditors of this exchange.
-   */
-  struct TEAH_AuditorListEntry *auditors_tail;
-
-  /**
-   * Key data of the exchange, only valid if
-   * @e handshake_complete is past stage #MHS_CERT.
-   */
-  struct TALER_EXCHANGE_Keys key_data;
-
-  /**
-   * Retry /keys frequency.
-   */
-  struct GNUNET_TIME_Relative retry_delay;
-
-  /**
-   * When does @e key_data expire?
-   */
-  struct GNUNET_TIME_Timestamp key_data_expiration;
-
-  /**
-   * Number of subsequent failed requests to /keys.
-   *
-   * Used to compute the CURL timeout for the request.
-   */
-  unsigned int keys_error_count;
-
-  /**
-   * Number of subsequent failed requests to /wire.
-   *
-   * Used to compute the CURL timeout for the request.
-   */
-  unsigned int wire_error_count;
-
-  /**
-   * Stage of the exchange's initialization routines.
-   */
-  enum ExchangeHandleState state;
-
-};
-
 
 /**
  * Function called for each auditor to give us a chance to possibly
@@ -156,9 +38,10 @@ struct TALER_EXCHANGE_Handle
  * @param auditor_pub public key of the auditor
  */
 typedef void
-(*TEAH_AuditorCallback)(void *cls,
-                        const char *auditor_url,
-                        const struct TALER_AuditorPublicKeyP *auditor_pub);
+(*TEAH_AuditorCallback)(
+  void *cls,
+  const char *auditor_url,
+  const struct TALER_AuditorPublicKeyP *auditor_pub);
 
 
 /**
@@ -171,50 +54,11 @@ typedef void
  * @param ac_cls closure for @a ac
  */
 void
-TEAH_get_auditors_for_dc (struct TALER_EXCHANGE_Keys *keys,
-                          TEAH_AuditorCallback ac,
-                          void *ac_cls);
-
+TEAH_get_auditors_for_dc (
+  struct TALER_EXCHANGE_Keys *keys,
+  TEAH_AuditorCallback ac,
+  void *ac_cls);
 
-/**
- * Get the context of a exchange.
- *
- * @param h the exchange handle to query
- * @return ctx context to execute jobs in
- */
-struct GNUNET_CURL_Context *
-TEAH_handle_to_context (struct TALER_EXCHANGE_Handle *h);
-
-
-/**
- * Check if the handle is ready to process requests.
- *
- * @param h the exchange handle to query
- * @return #GNUNET_YES if we are ready, #GNUNET_NO if not
- */
-enum GNUNET_GenericReturnValue
-TEAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h);
-
-/**
- * Check if the handle is ready to process requests.
- *
- * @param h the exchange handle to query
- * @return #GNUNET_YES if we are ready, #GNUNET_NO if not
- */
-enum GNUNET_GenericReturnValue
-TEAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h);
-
-
-/**
- * Obtain the URL to use for an API request.
- *
- * @param h the exchange handle to query
- * @param path Taler API path (i.e. "/reserve/withdraw")
- * @return the full URL to use with cURL
- */
-char *
-TEAH_path_to_url (struct TALER_EXCHANGE_Handle *h,
-                  const char *path);
 
 /* end of exchange_api_handle.h */
 #endif
diff --git a/src/testing/test_auditor_api.c b/src/testing/test_auditor_api.c
index 4e643f17..b5d6df0c 100644
--- a/src/testing/test_auditor_api.c
+++ b/src/testing/test_auditor_api.c
@@ -658,7 +658,7 @@ run (void *cls,
     TALER_TESTING_cmd_get_auditor ("get-auditor",
                                    cred.cfg,
                                    true),
-    TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
+    // FIXME: TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
     TALER_TESTING_cmd_exec_auditor_offline ("auditor-offline",
                                             config_file),
     CMD_RUN_AUDITOR ("virgin-auditor"),
diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c
index eb131d65..e8cc6659 100644
--- a/src/testing/test_exchange_api.c
+++ b/src/testing/test_exchange_api.c
@@ -1238,7 +1238,7 @@ run (void *cls,
                                       cred.cfg,
                                       true,
                                       true),
-      TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
+      // FIXME: TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
       TALER_TESTING_cmd_batch ("wire",
                                wire),
       TALER_TESTING_cmd_batch ("withdraw",
diff --git a/src/testing/test_exchange_api_keys_cherry_picking.c 
b/src/testing/test_exchange_api_keys_cherry_picking.c
index b463eea8..11e18e5c 100644
--- a/src/testing/test_exchange_api_keys_cherry_picking.c
+++ b/src/testing/test_exchange_api_keys_cherry_picking.c
@@ -67,7 +67,7 @@ run (void *cls,
                                     cred.cfg,
                                     true,
                                     true),
-    TALER_TESTING_cmd_check_keys_pull_all_keys ("initial-/keys"),
+    // FIXME: TALER_TESTING_cmd_check_keys_pull_all_keys ("initial-/keys"),
     TALER_TESTING_cmd_sleep ("sleep",
                              6 /* seconds */),
     TALER_TESTING_cmd_check_keys ("check-keys-1"),
diff --git a/src/testing/test_exchange_api_overlapping_keys_bug.c 
b/src/testing/test_exchange_api_overlapping_keys_bug.c
index 7cbdd9b8..3f7353b9 100644
--- a/src/testing/test_exchange_api_overlapping_keys_bug.c
+++ b/src/testing/test_exchange_api_overlapping_keys_bug.c
@@ -71,7 +71,7 @@ run (void *cls,
                                     cred.cfg,
                                     true,
                                     true),
-    TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
+    // FIXME: TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
     TALER_TESTING_cmd_check_keys ("first-download"),
     /* Causes GET /keys?last_denom_issue=0 */
     TALER_TESTING_cmd_check_keys_with_last_denom ("second-download",
diff --git a/src/testing/test_exchange_api_revocation.c 
b/src/testing/test_exchange_api_revocation.c
index c1c1b319..1cb544da 100644
--- a/src/testing/test_exchange_api_revocation.c
+++ b/src/testing/test_exchange_api_revocation.c
@@ -80,7 +80,7 @@ run (void *cls,
     TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys",
                                               config_file),
 #endif
-    TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
+    // FIXME: TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
     /**
      * Fill reserve with EUR:10.02, as withdraw fee is 1 ct per
      * config.
diff --git a/src/testing/test_exchange_management_api.c 
b/src/testing/test_exchange_management_api.c
index cabddcde..fded3f03 100644
--- a/src/testing/test_exchange_management_api.c
+++ b/src/testing/test_exchange_management_api.c
@@ -145,7 +145,7 @@ run (void *cls,
                                 false),
     TALER_TESTING_cmd_exec_offline_sign_keys ("download-future-keys",
                                               config_file),
-    TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
+    // FIXME: TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
     TALER_TESTING_cmd_end ()
   };
 
diff --git a/src/testing/test_exchange_p2p.c b/src/testing/test_exchange_p2p.c
index f5b11f60..689d2460 100644
--- a/src/testing/test_exchange_p2p.c
+++ b/src/testing/test_exchange_p2p.c
@@ -506,7 +506,7 @@ run (void *cls,
                                     cred.cfg,
                                     true,
                                     true),
-    TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
+    // FIXME: TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
     TALER_TESTING_cmd_batch ("withdraw",
                              withdraw),
     TALER_TESTING_cmd_batch ("push",
diff --git a/src/testing/test_kyc_api.c b/src/testing/test_kyc_api.c
index eb66d9c8..733a5e2f 100644
--- a/src/testing/test_kyc_api.c
+++ b/src/testing/test_kyc_api.c
@@ -525,7 +525,7 @@ run (void *cls,
                                     cred.cfg,
                                     true,
                                     true),
-    TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
+    // FIXME: TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
     TALER_TESTING_cmd_batch ("withdraw",
                              withdraw),
     TALER_TESTING_cmd_batch ("spend",
diff --git a/src/testing/test_taler_exchange_wirewatch.c 
b/src/testing/test_taler_exchange_wirewatch.c
index 86e104f4..54d25887 100644
--- a/src/testing/test_taler_exchange_wirewatch.c
+++ b/src/testing/test_taler_exchange_wirewatch.c
@@ -89,7 +89,7 @@ run (void *cls,
                                     cred.cfg,
                                     true,
                                     true),
-    TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
+    // FIXME: TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys"),
     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_auditor_deposit_confirmation.c 
b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
index 5c7b76a3..252e8f52 100644
--- a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
+++ b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
@@ -211,12 +211,8 @@ deposit_confirmation_run (void *cls,
   const struct TALER_EXCHANGE_Keys *keys;
   const struct TALER_EXCHANGE_SigningPublicKey *spk;
   const char *auditor_url;
-  struct TALER_EXCHANGE_Handle *exchange
-    = TALER_TESTING_get_exchange (is);
 
   (void) cmd;
-  if (NULL == exchange)
-    return;
   dcs->is = is;
   GNUNET_assert (NULL != dcs->deposit_reference);
   {
@@ -267,7 +263,7 @@ deposit_confirmation_run (void *cls,
                                                         dcs->coin_index,
                                                         &wire_deadline));
   GNUNET_assert (NULL != exchange_timestamp);
-  keys = TALER_EXCHANGE_get_keys (exchange);
+  keys = TALER_TESTING_get_keys (is);
   GNUNET_assert (NULL != keys);
   spk = TALER_EXCHANGE_get_signing_key_info (keys,
                                              exchange_pub);
diff --git a/src/testing/testing_api_cmd_batch_withdraw.c 
b/src/testing/testing_api_cmd_batch_withdraw.c
index 56af70b1..7f680949 100644
--- a/src/testing/testing_api_cmd_batch_withdraw.c
+++ b/src/testing/testing_api_cmd_batch_withdraw.c
@@ -253,12 +253,8 @@ batch_withdraw_run (void *cls,
   const struct TALER_TESTING_Command *create_reserve;
   const struct TALER_EXCHANGE_DenomPublicKey *dpk;
   struct TALER_EXCHANGE_WithdrawCoinInput wcis[ws->num_coins];
-  struct TALER_EXCHANGE_Handle *exchange
-    = TALER_TESTING_get_exchange (is);
 
   (void) cmd;
-  if (NULL == exchange)
-    return;
   ws->is = is;
   create_reserve
     = TALER_TESTING_interpreter_lookup_command (
@@ -281,7 +277,7 @@ batch_withdraw_run (void *cls,
   }
   if (NULL == ws->exchange_url)
     ws->exchange_url
-      = GNUNET_strdup (TALER_EXCHANGE_get_base_url (exchange));
+      = GNUNET_strdup (TALER_TESTING_get_exchange_url (is));
   ws->reserve_priv = *rp;
   GNUNET_CRYPTO_eddsa_key_get_public (&ws->reserve_priv.eddsa_priv,
                                       &ws->reserve_pub.eddsa_pub);
@@ -295,7 +291,7 @@ batch_withdraw_run (void *cls,
     struct TALER_EXCHANGE_WithdrawCoinInput *wci = &wcis[i];
 
     TALER_planchet_master_setup_random (&cs->ps);
-    dpk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (exchange),
+    dpk = TALER_TESTING_find_pk (TALER_TESTING_get_keys (is),
                                  &cs->amount,
                                  ws->age > 0);
     if (NULL == dpk)
diff --git a/src/testing/testing_api_cmd_check_keys.c 
b/src/testing/testing_api_cmd_check_keys.c
index f4ea126e..bb26d10a 100644
--- a/src/testing/testing_api_cmd_check_keys.c
+++ b/src/testing/testing_api_cmd_check_keys.c
@@ -27,6 +27,8 @@
 #include <gnunet/gnunet_curl_lib.h>
 #include "taler_testing_lib.h"
 
+// FIXME: duplicated with testing_api_cmd_connect_with_state
+// FIXME: this is now duplicated with testing_api_cmd_get_exchange!
 
 /**
  * State for a "check keys" CMD.
@@ -34,14 +36,9 @@
 struct CheckKeysState
 {
 
-  /**
-   * If this value is true, then the "cherry picking" facility is turned off;
-   * whole /keys is downloaded.
-   */
-  bool pull_all_keys;
-
   /**
    * Label of a command to use to derive the "last_denom_issue" date to use.
+   * FIXME: actually use this!
    */
   const char *last_denom_date_ref;
 
@@ -50,6 +47,11 @@ struct CheckKeysState
    */
   struct TALER_TESTING_Interpreter *is;
 
+  /**
+   * Our get keys operation.
+   */
+  struct TALER_EXCHANGE_GetKeysHandle *gkh;
+
   /**
    * Last denomination date we received when doing this request.
    */
@@ -66,7 +68,8 @@ struct CheckKeysState
  */
 static void
 keys_cb (void *cls,
-         const struct TALER_EXCHANGE_KeysResponse *kr)
+         const struct TALER_EXCHANGE_KeysResponse *kr,
+         struct TALER_EXCHANGE_Keys *keys)
 {
   struct CheckKeysState *cks = cls;
 
@@ -77,6 +80,8 @@ keys_cb (void *cls,
     return;
   }
   cks->my_denom_date = kr->details.ok.keys->last_denom_issue_date;
+  /* FIXME: expose keys (and exchange_url) via trait! */
+  TALER_EXCHANGE_keys_decref (keys);
   TALER_TESTING_interpreter_next (cks->is);
 }
 
@@ -94,64 +99,19 @@ check_keys_run (void *cls,
                 struct TALER_TESTING_Interpreter *is)
 {
   struct CheckKeysState *cks = cls;
-  struct TALER_EXCHANGE_Handle *exchange
-    = TALER_TESTING_get_exchange (is);
-  struct GNUNET_TIME_Timestamp rdate;
+  const char *exchange_url
+    = TALER_TESTING_get_exchange_url (is);
 
-  (void) cmd;
   cks->is = is;
-  if (NULL == exchange)
-    return;
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Triggering GET /keys, cmd `%s'\n",
               cmd->label);
-  if (NULL != cks->last_denom_date_ref)
-  {
-    if (0 == strcmp ("zero",
-                     cks->last_denom_date_ref))
-    {
-      TALER_LOG_DEBUG ("Forcing last_denom_date URL argument set to zero\n");
-      TALER_EXCHANGE_set_last_denom (exchange,
-                                     GNUNET_TIME_UNIT_ZERO_TS);
-    }
-    else
-    {
-      const struct GNUNET_TIME_Timestamp *last_denom_date;
-      const struct TALER_TESTING_Command *ref;
-
-      ref = TALER_TESTING_interpreter_lookup_command (is,
-                                                      
cks->last_denom_date_ref);
-      if (NULL == ref)
-      {
-        GNUNET_break (0);
-        TALER_TESTING_interpreter_fail (is);
-        return;
-      }
-      if (GNUNET_OK !=
-          TALER_TESTING_get_trait_timestamp (ref,
-                                             0,
-                                             &last_denom_date))
-      {
-        GNUNET_break (0);
-        TALER_TESTING_interpreter_fail (is);
-        return;
-      }
-
-      TALER_LOG_DEBUG ("Forcing last_denom_date URL argument\n");
-      TALER_EXCHANGE_set_last_denom (exchange,
-                                     *last_denom_date);
-    }
-  }
-
-  rdate = TALER_EXCHANGE_check_keys_current (
-    exchange,
-    cks->pull_all_keys
-      ? TALER_EXCHANGE_CKF_FORCE_ALL_NOW
-    : TALER_EXCHANGE_CKF_FORCE_DOWNLOAD,
+  cks->gkh = TALER_EXCHANGE_get_keys (
+    TALER_TESTING_interpreter_get_context (is),
+    exchange_url,
+    NULL, /* FIXME: get form last_denom_date_ref! */
     &keys_cb,
     cks);
-  /* Redownload /keys.  */
-  GNUNET_break (GNUNET_TIME_absolute_is_zero (rdate.abs_time));
 }
 
 
@@ -168,6 +128,11 @@ check_keys_cleanup (void *cls,
   struct CheckKeysState *cks = cls;
 
   (void) cmd;
+  if (NULL != cks->gkh)
+  {
+    TALER_EXCHANGE_get_keys_cancel (cks->gkh);
+    cks->gkh = NULL;
+  }
   GNUNET_free (cks);
 }
 
@@ -204,10 +169,21 @@ check_keys_traits (void *cls,
 
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_check_keys (const char *label)
+{
+  return TALER_TESTING_cmd_check_keys_with_last_denom (label,
+                                                       NULL);
+}
+
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_check_keys_with_last_denom (
+  const char *label,
+  const char *last_denom_date_ref)
 {
   struct CheckKeysState *cks;
 
   cks = GNUNET_new (struct CheckKeysState);
+  cks->last_denom_date_ref = last_denom_date_ref;
   {
     struct TALER_TESTING_Command cmd = {
       .cls = cks,
@@ -222,30 +198,4 @@ TALER_TESTING_cmd_check_keys (const char *label)
 }
 
 
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_check_keys_pull_all_keys (const char *label)
-{
-  struct TALER_TESTING_Command cmd
-    = TALER_TESTING_cmd_check_keys (label);
-  struct CheckKeysState *cks = cmd.cls;
-
-  cks->pull_all_keys = true;
-  return cmd;
-}
-
-
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_check_keys_with_last_denom (
-  const char *label,
-  const char *last_denom_date_ref)
-{
-  struct TALER_TESTING_Command cmd
-    = TALER_TESTING_cmd_check_keys (label);
-  struct CheckKeysState *cks = cmd.cls;
-
-  cks->last_denom_date_ref = last_denom_date_ref;
-  return cmd;
-}
-
-
 /* end of testing_api_cmd_check_keys.c */
diff --git a/src/testing/testing_api_cmd_connect_with_state.c 
b/src/testing/testing_api_cmd_connect_with_state.c
index 95e860f1..81587248 100644
--- a/src/testing/testing_api_cmd_connect_with_state.c
+++ b/src/testing/testing_api_cmd_connect_with_state.c
@@ -26,6 +26,9 @@
 #include "taler_testing_lib.h"
 
 
+// FIXME: this is now duplicated with testing_api_cmd_check_keys!
+// FIXME: this is now duplicated with testing_api_cmd_get_exchange!
+
 /**
  * Internal state for a connect-with-state CMD.
  */
@@ -46,13 +49,19 @@ struct ConnectWithStateState
   /**
    * New exchange handle.
    */
-  struct TALER_EXCHANGE_Handle *exchange;
+  struct TALER_EXCHANGE_GetKeysHandle *exchange;
+
+  /**
+   * Keys handle.
+   */
+  struct TALER_EXCHANGE_Keys *keys;
 };
 
 
 static void
 cert_cb (void *cls,
-         const struct TALER_EXCHANGE_KeysResponse *kr)
+         const struct TALER_EXCHANGE_KeysResponse *kr,
+         struct TALER_EXCHANGE_Keys *keys)
 {
   struct ConnectWithStateState *cwss = cls;
   struct TALER_TESTING_Interpreter *is = cwss->is;
@@ -72,6 +81,7 @@ cert_cb (void *cls,
     TALER_TESTING_interpreter_fail (is);
     return;
   }
+  cwss->keys = keys;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Got %d DK from /keys\n",
               kr->details.ok.keys->num_denom_keys);
@@ -113,14 +123,12 @@ connect_with_state_run (void *cls,
                  TALER_TESTING_get_trait_exchange_url (state_cmd,
                                                        &exchange_url));
   cwss->exchange
-    = TALER_EXCHANGE_connect (
+    = TALER_EXCHANGE_get_keys (
         TALER_TESTING_interpreter_get_context (is),
         exchange_url,
+        TALER_EXCHANGE_keys_from_json (serialized_keys),
         &cert_cb,
-        cwss,
-        TALER_EXCHANGE_OPTION_DATA,
-        serialized_keys,
-        TALER_EXCHANGE_OPTION_END);
+        cwss);
 }
 
 
@@ -141,7 +149,8 @@ connect_with_state_traits (void *cls,
 {
   struct ConnectWithStateState *cwss = cls;
   struct TALER_TESTING_Trait traits[] = {
-    TALER_TESTING_make_trait_exchange (cwss->exchange),
+    TALER_TESTING_make_trait_keys (cwss->keys),
+    // FIXME: also expose exchange_url as trait
     TALER_TESTING_trait_end ()
   };
 
@@ -165,6 +174,13 @@ connect_with_state_cleanup (void *cls,
 {
   struct ConnectWithStateState *cwss = cls;
 
+  TALER_EXCHANGE_keys_decref (cwss->keys);
+  cwss->keys = NULL;
+  if (NULL != cwss->exchange)
+  {
+    TALER_EXCHANGE_get_keys_cancel (cwss->exchange);
+    cwss->exchange = NULL;
+  }
   GNUNET_free (cwss);
 }
 
diff --git a/src/testing/testing_api_cmd_get_exchange.c 
b/src/testing/testing_api_cmd_get_exchange.c
index 2822616c..b6634286 100644
--- a/src/testing/testing_api_cmd_get_exchange.c
+++ b/src/testing/testing_api_cmd_get_exchange.c
@@ -46,7 +46,12 @@ struct GetExchangeState
   /**
    * Exchange handle we produced.
    */
-  struct TALER_EXCHANGE_Handle *exchange;
+  struct TALER_EXCHANGE_GetKeysHandle *exchange;
+
+  /**
+   * Keys of the exchange.
+   */
+  struct TALER_EXCHANGE_Keys *keys;
 
   /**
    * URL of the exchange.
@@ -67,12 +72,15 @@ struct GetExchangeState
 
 static void
 cert_cb (void *cls,
-         const struct TALER_EXCHANGE_KeysResponse *kr)
+         const struct TALER_EXCHANGE_KeysResponse *kr,
+         struct TALER_EXCHANGE_Keys *keys)
 {
   struct GetExchangeState *ges = cls;
   const struct TALER_EXCHANGE_HttpResponse *hr = &kr->hr;
   struct TALER_TESTING_Interpreter *is = ges->is;
 
+  ges->exchange = NULL;
+  ges->keys = keys;
   switch (hr->http_status)
   {
   case MHD_HTTP_OK:
@@ -85,8 +93,9 @@ cert_cb (void *cls,
     return;
   default:
     GNUNET_break (0);
-    TALER_EXCHANGE_disconnect (ges->exchange);
-    ges->exchange = NULL;
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "/keys responded with HTTP status %u\n",
+                hr->http_status);
     if (ges->wait_for_keys)
     {
       ges->wait_for_keys = false;
@@ -133,11 +142,11 @@ get_exchange_run (void *cls,
   }
   ges->is = is;
   ges->exchange
-    = TALER_EXCHANGE_connect (TALER_TESTING_interpreter_get_context (is),
-                              ges->exchange_url,
-                              &cert_cb,
-                              ges,
-                              TALER_EXCHANGE_OPTION_END);
+    = TALER_EXCHANGE_get_keys (TALER_TESTING_interpreter_get_context (is),
+                               ges->exchange_url,
+                               NULL,
+                               &cert_cb,
+                               ges);
   if (NULL == ges->exchange)
   {
     GNUNET_break (0);
@@ -163,9 +172,11 @@ get_exchange_cleanup (void *cls,
 
   if (NULL != ges->exchange)
   {
-    TALER_EXCHANGE_disconnect (ges->exchange);
+    TALER_EXCHANGE_get_keys_cancel (ges->exchange);
     ges->exchange = NULL;
   }
+  TALER_EXCHANGE_keys_decref (ges->keys);
+  ges->keys = NULL;
   GNUNET_free (ges->master_priv_file);
   GNUNET_free (ges->exchange_url);
   GNUNET_free (ges);
@@ -189,16 +200,13 @@ get_exchange_traits (void *cls,
 {
   struct GetExchangeState *ges = cls;
   unsigned int off = (NULL == ges->master_priv_file) ? 1 : 0;
-  struct TALER_EXCHANGE_Keys *keys
-    = TALER_EXCHANGE_get_keys (ges->exchange);
 
-  if (NULL != keys)
+  if (NULL != ges->keys)
   {
     struct TALER_TESTING_Trait traits[] = {
       TALER_TESTING_make_trait_master_priv (&ges->master_priv),
-      TALER_TESTING_make_trait_master_pub (&keys->master_pub),
-      TALER_TESTING_make_trait_exchange (ges->exchange),
-      TALER_TESTING_make_trait_keys (keys),
+      TALER_TESTING_make_trait_master_pub (&ges->keys->master_pub),
+      TALER_TESTING_make_trait_keys (ges->keys),
       TALER_TESTING_make_trait_exchange_url (ges->exchange_url),
       TALER_TESTING_trait_end ()
     };
@@ -212,7 +220,6 @@ get_exchange_traits (void *cls,
   {
     struct TALER_TESTING_Trait traits[] = {
       TALER_TESTING_make_trait_master_priv (&ges->master_priv),
-      TALER_TESTING_make_trait_exchange (ges->exchange),
       TALER_TESTING_make_trait_exchange_url (ges->exchange_url),
       TALER_TESTING_trait_end ()
     };
diff --git a/src/testing/testing_api_cmd_purse_deposit.c 
b/src/testing/testing_api_cmd_purse_deposit.c
index 8bddc53a..eafe3342 100644
--- a/src/testing/testing_api_cmd_purse_deposit.c
+++ b/src/testing/testing_api_cmd_purse_deposit.c
@@ -133,11 +133,8 @@ deposit_cb (void *cls,
             const struct TALER_EXCHANGE_PurseDepositResponse *dr)
 {
   struct PurseDepositState *ds = cls;
-  struct TALER_EXCHANGE_Handle *exchange
-    = TALER_TESTING_get_exchange (ds->is);
 
   ds->dh = NULL;
-  GNUNET_assert (NULL != exchange);
   if (ds->expected_response_code != dr->hr.http_status)
   {
     TALER_TESTING_unexpected_status (ds->is,
@@ -197,10 +194,10 @@ deposit_cb (void *cls,
       /* Deposits complete, create trait! */
       ds->reserve_history.type = TALER_EXCHANGE_RTT_MERGE;
       {
-        const struct TALER_EXCHANGE_Keys *keys;
+        struct TALER_EXCHANGE_Keys *keys;
         const struct TALER_EXCHANGE_GlobalFee *gf;
 
-        keys = TALER_EXCHANGE_get_keys (exchange);
+        keys = TALER_TESTING_get_keys (ds->is);
         GNUNET_assert (NULL != keys);
         gf = TALER_EXCHANGE_get_global_fee (keys,
                                             *merge_timestamp);
diff --git a/src/testing/testing_api_cmd_refresh.c 
b/src/testing/testing_api_cmd_refresh.c
index dfaf31fd..6449c538 100644
--- a/src/testing/testing_api_cmd_refresh.c
+++ b/src/testing/testing_api_cmd_refresh.c
@@ -371,12 +371,8 @@ reveal_cb (void *cls,
   struct RefreshRevealState *rrs = cls;
   const struct TALER_EXCHANGE_HttpResponse *hr = &rr->hr;
   const struct TALER_TESTING_Command *melt_cmd;
-  struct TALER_EXCHANGE_Handle *exchange
-    = TALER_TESTING_get_exchange (rrs->is);
 
   rrs->rrh = NULL;
-  if (NULL == exchange)
-    return;
   if (rrs->expected_response_code != hr->http_status)
   {
     if (0 != rrs->do_retry)
@@ -1006,12 +1002,8 @@ melt_run (void *cls,
     NULL
   };
   const char **melt_fresh_amounts;
-  struct TALER_EXCHANGE_Handle *exchange
-    = TALER_TESTING_get_exchange (is);
 
   rms->cmd = cmd;
-  if (NULL == exchange)
-    return;
   if (NULL == (melt_fresh_amounts = rms->melt_fresh_amounts))
     melt_fresh_amounts = default_melt_fresh_amounts;
   rms->is = is;
@@ -1115,7 +1107,7 @@ melt_run (void *cls,
         TALER_TESTING_interpreter_fail (rms->is);
         return;
       }
-      fresh_pk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (exchange),
+      fresh_pk = TALER_TESTING_find_pk (TALER_TESTING_get_keys (rms->is),
                                         &fresh_amount,
                                         age_restricted);
       if (NULL == fresh_pk)
diff --git a/src/testing/testing_api_cmd_reserve_history.c 
b/src/testing/testing_api_cmd_reserve_history.c
index 6e68bbe2..a7df69e6 100644
--- a/src/testing/testing_api_cmd_reserve_history.c
+++ b/src/testing/testing_api_cmd_reserve_history.c
@@ -230,19 +230,15 @@ reserve_history_cb (void *cls,
   struct HistoryState *ss = cls;
   struct TALER_TESTING_Interpreter *is = ss->is;
   struct TALER_Amount eb;
-  struct TALER_EXCHANGE_Handle *exchange
-    = TALER_TESTING_get_exchange (is);
 
   ss->rsh = NULL;
-  if (NULL == exchange)
-    return;
   if (MHD_HTTP_OK == rs->hr.http_status)
   {
-    const struct TALER_EXCHANGE_Keys *keys;
+    struct TALER_EXCHANGE_Keys *keys;
     const struct TALER_EXCHANGE_GlobalFee *gf;
 
     ss->reserve_history.type = TALER_EXCHANGE_RTT_HISTORY;
-    keys = TALER_EXCHANGE_get_keys (exchange);
+    keys = TALER_TESTING_get_keys (is);
     GNUNET_assert (NULL != keys);
     gf = TALER_EXCHANGE_get_global_fee (keys,
                                         rs->ts);
@@ -343,11 +339,7 @@ history_run (void *cls,
 {
   struct HistoryState *ss = cls;
   const struct TALER_TESTING_Command *create_reserve;
-  struct TALER_EXCHANGE_Handle *exchange
-    = TALER_TESTING_get_exchange (is);
 
-  if (NULL == exchange)
-    return;
   ss->is = is;
   create_reserve
     = TALER_TESTING_interpreter_lookup_command (is,
diff --git a/src/testing/testing_api_cmd_reserve_open.c 
b/src/testing/testing_api_cmd_reserve_open.c
index a78662c3..189d06b2 100644
--- a/src/testing/testing_api_cmd_reserve_open.c
+++ b/src/testing/testing_api_cmd_reserve_open.c
@@ -165,11 +165,7 @@ open_run (void *cls,
   struct OpenState *ss = cls;
   const struct TALER_TESTING_Command *create_reserve;
   struct TALER_EXCHANGE_PurseDeposit cp[GNUNET_NZL (ss->cpl)];
-  struct TALER_EXCHANGE_Handle *exchange
-    = TALER_TESTING_get_exchange (is);
 
-  if (NULL == exchange)
-    return;
   ss->is = is;
   create_reserve
     = TALER_TESTING_interpreter_lookup_command (is,
diff --git a/src/testing/testing_api_cmd_reserve_purse.c 
b/src/testing/testing_api_cmd_reserve_purse.c
index 7a356c23..511e2d49 100644
--- a/src/testing/testing_api_cmd_reserve_purse.c
+++ b/src/testing/testing_api_cmd_reserve_purse.c
@@ -184,12 +184,8 @@ purse_run (void *cls,
   struct ReservePurseState *ds = cls;
   const struct TALER_ReservePrivateKeyP *reserve_priv;
   const struct TALER_TESTING_Command *ref;
-  struct TALER_EXCHANGE_Handle *exchange
-    = TALER_TESTING_get_exchange (is);
 
   (void) cmd;
-  if (NULL == exchange)
-    return;
   ds->is = is;
   ref = TALER_TESTING_interpreter_lookup_command (ds->is,
                                                   ds->reserve_ref);
diff --git a/src/testing/testing_api_cmd_reserve_status.c 
b/src/testing/testing_api_cmd_reserve_status.c
index 2da1bf74..001582ed 100644
--- a/src/testing/testing_api_cmd_reserve_status.c
+++ b/src/testing/testing_api_cmd_reserve_status.c
@@ -314,11 +314,7 @@ status_run (void *cls,
 {
   struct StatusState *ss = cls;
   const struct TALER_TESTING_Command *create_reserve;
-  struct TALER_EXCHANGE_Handle *exchange
-    = TALER_TESTING_get_exchange (is);
 
-  if (NULL == exchange)
-    return;
   ss->is = is;
   create_reserve
     = TALER_TESTING_interpreter_lookup_command (is,
diff --git a/src/testing/testing_api_cmd_serialize_keys.c 
b/src/testing/testing_api_cmd_serialize_keys.c
index 9e5a25c4..13464dff 100644
--- a/src/testing/testing_api_cmd_serialize_keys.c
+++ b/src/testing/testing_api_cmd_serialize_keys.c
@@ -58,12 +58,12 @@ serialize_keys_run (void *cls,
                     struct TALER_TESTING_Interpreter *is)
 {
   struct SerializeKeysState *sks = cls;
-  struct TALER_EXCHANGE_Handle *exchange
-    = TALER_TESTING_get_exchange (is);
+  struct TALER_EXCHANGE_Keys *keys
+    = TALER_TESTING_get_keys (is);
 
-  if (NULL == exchange)
+  if (NULL == keys)
     return;
-  sks->keys = TALER_EXCHANGE_serialize_data (exchange);
+  sks->keys = TALER_EXCHANGE_keys_to_json (keys);
   if (NULL == sks->keys)
   {
     GNUNET_break (0);
@@ -71,7 +71,7 @@ serialize_keys_run (void *cls,
   }
   sks->exchange_url
     = GNUNET_strdup (
-        TALER_EXCHANGE_get_base_url (exchange));
+        TALER_TESTING_get_exchange_url (is));
   TALER_TESTING_interpreter_next (is);
 }
 
diff --git a/src/testing/testing_api_cmd_transfer_get.c 
b/src/testing/testing_api_cmd_transfer_get.c
index 37fe736b..da3585d6 100644
--- a/src/testing/testing_api_cmd_transfer_get.c
+++ b/src/testing/testing_api_cmd_transfer_get.c
@@ -309,12 +309,8 @@ track_transfer_run (void *cls,
   struct TrackTransferState *tts = cls;
   struct TALER_WireTransferIdentifierRawP wtid;
   const struct TALER_WireTransferIdentifierRawP *wtid_ptr;
-  struct TALER_EXCHANGE_Handle *exchange
-    = TALER_TESTING_get_exchange (is);
 
   tts->cmd = cmd;
-  if (NULL == exchange)
-    return;
   /* If no reference is given, we'll use a all-zeros
    * WTID */
   memset (&wtid,
diff --git a/src/testing/testing_api_cmd_withdraw.c 
b/src/testing/testing_api_cmd_withdraw.c
index a6315f91..3e735ad0 100644
--- a/src/testing/testing_api_cmd_withdraw.c
+++ b/src/testing/testing_api_cmd_withdraw.c
@@ -347,7 +347,6 @@ withdraw_run (void *cls,
   const struct TALER_ReservePrivateKeyP *rp;
   const struct TALER_TESTING_Command *create_reserve;
   const struct TALER_EXCHANGE_DenomPublicKey *dpk;
-  struct TALER_EXCHANGE_Handle *exchange;
 
   ws->cmd = cmd;
   ws->is = is;
@@ -369,12 +368,9 @@ withdraw_run (void *cls,
     TALER_TESTING_interpreter_fail (is);
     return;
   }
-  exchange = TALER_TESTING_get_exchange (is);
-  if (NULL == exchange)
-    return;
   if (NULL == ws->exchange_url)
     ws->exchange_url
-      = GNUNET_strdup (TALER_EXCHANGE_get_base_url (exchange));
+      = GNUNET_strdup (TALER_TESTING_get_exchange_url (is));
   ws->reserve_priv = *rp;
   GNUNET_CRYPTO_eddsa_key_get_public (&ws->reserve_priv.eddsa_priv,
                                       &ws->reserve_pub.eddsa_pub);
diff --git a/src/testing/testing_api_traits.c b/src/testing/testing_api_traits.c
index d00a8d8c..799ae671 100644
--- a/src/testing/testing_api_traits.c
+++ b/src/testing/testing_api_traits.c
@@ -75,33 +75,6 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait 
*traits,
 }
 
 
-struct TALER_EXCHANGE_Handle *
-TALER_TESTING_get_exchange (struct TALER_TESTING_Interpreter *is)
-{
-  struct TALER_EXCHANGE_Handle *exchange;
-  const struct TALER_TESTING_Command *exchange_cmd;
-
-  exchange_cmd
-    = TALER_TESTING_interpreter_get_command (is,
-                                             "exchange");
-  if (NULL == exchange_cmd)
-  {
-    GNUNET_break (0);
-    TALER_TESTING_interpreter_fail (is);
-    return NULL;
-  }
-  if (GNUNET_OK !=
-      TALER_TESTING_get_trait_exchange (exchange_cmd,
-                                        &exchange))
-  {
-    GNUNET_break (0);
-    TALER_TESTING_interpreter_fail (is);
-    return NULL;
-  }
-  return exchange;
-}
-
-
 const char *
 TALER_TESTING_get_exchange_url (struct TALER_TESTING_Interpreter *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]