gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-exchange] branch master updated: fix #5315, includin


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] branch master updated: fix #5315, including more testing
Date: Sat, 15 Sep 2018 22:20:10 +0200

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

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new 528faeab fix #5315, including more testing
528faeab is described below

commit 528faeab81ba240329140eb1587ccc542736c1dc
Author: Christian Grothoff <address@hidden>
AuthorDate: Sat Sep 15 22:20:07 2018 +0200

    fix #5315, including more testing
---
 src/exchange-lib/Makefile.am                       |   29 +-
 src/exchange-lib/exchange_api_handle.c             |   52 +-
 src/exchange-lib/test_exchange_api.c               | 4035 --------------------
 .../test_exchange_api_keys_cherry_picking.c        |  803 ----
 .../test_exchange_api_keys_cherry_picking.conf     |   12 +-
 .../test_exchange_api_keys_cherry_picking_new.c    |   25 +-
 src/exchange-lib/testing_api_cmd_check_keys.c      |   11 +-
 src/exchange-tools/taler-exchange-keyup.c          |   14 +-
 src/exchange/taler-exchange-httpd_keystate.c       |    9 +
 src/include/taler_exchange_service.h               |   15 +-
 10 files changed, 83 insertions(+), 4922 deletions(-)

diff --git a/src/exchange-lib/Makefile.am b/src/exchange-lib/Makefile.am
index 9dbd2256..8ac43ea8 100644
--- a/src/exchange-lib/Makefile.am
+++ b/src/exchange-lib/Makefile.am
@@ -48,6 +48,7 @@ libtalertesting_la_SOURCES = \
   testing_api_cmd_refund.c \
   testing_api_cmd_status.c \
   testing_api_cmd_deposit.c \
+  testing_api_cmd_sleep.c \
   testing_api_cmd_refresh.c \
   testing_api_cmd_track.c \
   testing_api_cmd_bank_check.c \
@@ -95,9 +96,7 @@ endif
 
 check_PROGRAMS = \
   test_exchange_api_keys_cherry_picking_new \
-  test_exchange_api_new \
-  test_exchange_api_keys_cherry_picking \
-  test_exchange_api
+  test_exchange_api_new
 
 if HAVE_TWISTER
   check_PROGRAMS += \
@@ -126,19 +125,6 @@ AM_TESTS_ENVIRONMENT=export 
TALER_PREFIX=$${TALER_PREFIX:address@hidden@};export PATH=
 TESTS = \
   $(check_PROGRAMS)
 
-test_exchange_api_SOURCES = \
-  test_exchange_api.c
-test_exchange_api_LDADD = \
-  libtalerexchange.la \
-  $(LIBGCRYPT_LIBS) \
-  $(top_builddir)/src/bank-lib/libtalerfakebank.la \
-  $(top_builddir)/src/bank-lib/libtalerbank.la \
-  $(top_builddir)/src/json/libtalerjson.la \
-  $(top_builddir)/src/util/libtalerutil.la \
-  -lgnunetcurl \
-  -lgnunetutil \
-  -ljansson
-
 test_exchange_api_new_SOURCES = \
   test_exchange_api_new.c
 test_exchange_api_new_LDADD = \
@@ -166,17 +152,6 @@ test_exchange_api_keys_cherry_picking_new_LDADD = \
   -lgnunetutil \
   -ljansson
 
-test_exchange_api_keys_cherry_picking_SOURCES = \
-  test_exchange_api_keys_cherry_picking.c
-test_exchange_api_keys_cherry_picking_LDADD = \
-  libtalerexchange.la \
-  $(LIBGCRYPT_LIBS) \
-  $(top_builddir)/src/json/libtalerjson.la \
-  $(top_builddir)/src/util/libtalerutil.la \
-  -lgnunetcurl \
-  -lgnunetutil \
-  -ljansson
-
 EXTRA_DIST = \
   test_exchange_api_home/.local/share/taler/exchange/offline-keys/master.priv \
   test_exchange_api_home/.config/taler/test.json \
diff --git a/src/exchange-lib/exchange_api_handle.c 
b/src/exchange-lib/exchange_api_handle.c
index ba1f8c21..83dda20f 100644
--- a/src/exchange-lib/exchange_api_handle.c
+++ b/src/exchange-lib/exchange_api_handle.c
@@ -426,14 +426,15 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
                       &kv.auditor_url_hash);
   kv.master = key_data->master_pub;
   len = json_array_size (keys);
-  auditor->denom_keys = GNUNET_new_array (len,
-                                          const struct 
TALER_EXCHANGE_DenomPublicKey *);
+  auditor->denom_key_offsets = GNUNET_new_array (len,
+                                                 unsigned int);
   i = 0;
   off = 0;
   json_array_foreach (keys, i, key) {
     struct TALER_AuditorSignatureP auditor_sig;
     struct GNUNET_HashCode denom_h;
     const struct TALER_EXCHANGE_DenomPublicKey *dk;
+    unsigned int dk_off;
     struct GNUNET_JSON_Specification kspec[] = {
       GNUNET_JSON_spec_fixed_auto ("denom_pub_h",
                                    &denom_h),
@@ -451,6 +452,7 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
       continue;
     }
     dk = NULL;
+    dk_off = UINT_MAX;
     for (unsigned int j=0;j<key_data->num_denom_keys;j++)
     {
       if (0 == memcmp (&denom_h,
@@ -458,6 +460,7 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
                        sizeof (struct GNUNET_HashCode)))
       {
         dk = &key_data->denom_keys[j];
+        dk_off = j;
         break;
       }
     }
@@ -492,7 +495,7 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
       GNUNET_JSON_parse_free (spec);
       return GNUNET_SYSERR;
     }
-    auditor->denom_keys[off] = dk;
+    auditor->denom_key_offsets[off] = dk_off;
     off++;
   }
   auditor->num_denom_keys = off;
@@ -628,7 +631,8 @@ decode_keys_json (const json_t *resp_obj,
     unsigned int index;
 
     EXITIF (NULL == (denom_keys_array =
-                     json_object_get (resp_obj, "denoms")));
+                     json_object_get (resp_obj,
+                                      "denoms")));
     EXITIF (JSON_ARRAY != json_typeof (denom_keys_array));
 
     index = 0;
@@ -694,6 +698,7 @@ decode_keys_json (const json_t *resp_obj,
       for (unsigned int j=0;j<key_data->num_auditors;j++)
       {
         struct TALER_EXCHANGE_AuditorInformation *aix = &key_data->auditors[j];
+
        if (0 == memcmp (&ai.auditor_pub,
                         &aix->auditor_pub,
                         sizeof (struct TALER_AuditorPublicKeyP)))
@@ -701,12 +706,12 @@ decode_keys_json (const json_t *resp_obj,
          found = true;
           /* Merge denomination key signatures of downloaded /keys into 
existing
              auditor information 'aix'. */
-          GNUNET_array_grow (aix->denom_keys,
+          GNUNET_array_grow (aix->denom_key_offsets,
                              aix->num_denom_keys,
                              aix->num_denom_keys + ai.num_denom_keys);
-          memcpy (&aix->denom_keys[aix->num_denom_keys - ai.num_denom_keys],
-                  ai.denom_keys,
-                  ai.num_denom_keys * sizeof (struct 
TALER_EXCHANGE_DenomPublicKey *));
+          memcpy (&aix->denom_key_offsets[aix->num_denom_keys - 
ai.num_denom_keys],
+                  ai.denom_key_offsets,
+                  ai.num_denom_keys * sizeof (unsigned int));
          break;
        }
       }
@@ -762,7 +767,7 @@ free_key_data (struct TALER_EXCHANGE_Keys *key_data)
                      0);
   for (unsigned int i=0;i<key_data->num_auditors;i++)
   {
-    GNUNET_array_grow (key_data->auditors[i].denom_keys,
+    GNUNET_array_grow (key_data->auditors[i].denom_key_offsets,
                        key_data->auditors[i].num_denom_keys,
                        0);
     GNUNET_free (key_data->auditors[i].auditor_url);
@@ -827,10 +832,11 @@ keys_completed_cb (void *cls,
   struct TALER_EXCHANGE_Keys kd_old;
   enum TALER_EXCHANGE_VersionCompatibility vc;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
               "Received keys from URL `%s' with status %ld.\n",
               kr->url,
               response_code);
+  json_dumpf (resp_obj, stderr, 0);
   kd_old = exchange->key_data;
   memset (&kd,
           0,
@@ -878,17 +884,12 @@ keys_completed_cb (void *cls,
 
       anew->auditor_pub = aold->auditor_pub;
       anew->auditor_url = GNUNET_strdup (aold->auditor_url);
-      GNUNET_array_grow (anew->denom_keys,
+      GNUNET_array_grow (anew->denom_key_offsets,
                          anew->num_denom_keys,
                          aold->num_denom_keys);
-      for (unsigned int j=0;j<aold->num_denom_keys;j++)
-      {
-        /* offsets will map 1:1 */
-        unsigned int off = aold->denom_keys[j] - kd_old.denom_keys;
-        /* FIXME(#5315): this should not be an assert! */
-        GNUNET_assert (off < kd_old.num_denom_keys);
-        anew->denom_keys[j] = &kd.denom_keys[off];
-      }
+      memcpy (anew->denom_key_offsets,
+              aold->denom_key_offsets,
+              aold->num_denom_keys * sizeof (unsigned int));
     }
 
     if (GNUNET_OK !=
@@ -921,10 +922,10 @@ keys_completed_cb (void *cls,
                        NULL,
                       vc);
     if (NULL != exchange->key_data_raw)
-      {
-        json_decref (exchange->key_data_raw);
-        exchange->key_data_raw = NULL;
-      }
+    {
+      json_decref (exchange->key_data_raw);
+      exchange->key_data_raw = NULL;
+    }
     free_key_data (&kd_old);
     return;
   }
@@ -1157,13 +1158,10 @@ request_keys (void *cls)
   kr = GNUNET_new (struct KeysRequest);
   kr->exchange = exchange;
   if (GNUNET_YES ==
-      MAH_handle_is_ready (exchange) && 
!TALER_EXCHANGE_API_DISABLE_CHERRYPICKING)
+      MAH_handle_is_ready (exchange))
   {
     char *arg;
 
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Doing cherry-picking\n");
-
     GNUNET_asprintf (&arg,
                     "/keys?last_issue_date=%llu",
                     (unsigned long long) 
exchange->key_data.last_denom_issue_date.abs_value_us / 1000000LLU);
diff --git a/src/exchange-lib/test_exchange_api.c 
b/src/exchange-lib/test_exchange_api.c
deleted file mode 100644
index a96e4996..00000000
--- a/src/exchange-lib/test_exchange_api.c
+++ /dev/null
@@ -1,4035 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014-2017 GNUnet e.V. and Inria
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along with
-  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file exchange/test_exchange_api.c
- * @brief testcase to test exchange's HTTP API interface
- * @author Sree Harsha Totakura <address@hidden>
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "taler_util.h"
-#include "taler_signatures.h"
-#include "taler_exchange_service.h"
-#include "taler_json_lib.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <microhttpd.h>
-#include "taler_bank_service.h"
-#include "taler_fakebank_lib.h"
-
-
-/**
- * Is the configuration file is set to include wire format 'test'?
- */
-#define WIRE_TEST 1
-
-/**
- * Is the configuration file is set to include wire format 'ebics'?
- * Requires that EBICS /history function is implemented, which it
- * is currently not.  Once it is, set ENABLE_CREDIT to YES in the
- * configuration and then set this option to 1.
- */
-#define WIRE_EBICS 0
-
-/**
- * Account number of the exchange at the bank.
- */
-#define EXCHANGE_ACCOUNT_NO 2
-
-/**
- * Main execution context for the main loop.
- */
-static struct GNUNET_CURL_Context *ctx;
-
-/**
- * Handle to access the exchange.
- */
-static struct TALER_EXCHANGE_Handle *exchange;
-
-/**
- * Context for running the CURL event loop.
- */
-static struct GNUNET_CURL_RescheduleContext *rc;
-
-/**
- * Handle to the exchange process.
- */
-static struct GNUNET_OS_Process *exchanged;
-
-/**
- * Task run on timeout.
- */
-static struct GNUNET_SCHEDULER_Task *timeout_task;
-
-/**
- * Handle to our fakebank.
- */
-static struct TALER_FAKEBANK_Handle *fakebank;
-
-/**
- * Result of the testcases, #GNUNET_OK on success
- */
-static int result;
-
-
-/**
- * Opcodes for the interpreter.
- */
-enum OpCode
-{
-  /**
-   * Termination code, stops the interpreter loop (with success).
-   */
-  OC_END = 0,
-
-  /**
-   * Add funds to a reserve by (faking) incoming wire transfer.
-   */
-  OC_ADMIN_ADD_INCOMING,
-
-  /**
-   * Check status of a reserve.
-   */
-  OC_WITHDRAW_STATUS,
-
-  /**
-   * Withdraw a coin from a reserve.
-   */
-  OC_WITHDRAW_SIGN,
-
-  /**
-   * Deposit a coin (pay with it).
-   */
-  OC_DEPOSIT,
-
-  /**
-   * Melt a (set of) coins.
-   */
-  OC_REFRESH_MELT,
-
-  /**
-   * Complete melting session by withdrawing melted coins.
-   */
-  OC_REFRESH_REVEAL,
-
-  /**
-   * Verify exchange's /refresh/link by linking original private key to
-   * results from #OC_REFRESH_REVEAL step.
-   */
-  OC_REFRESH_LINK,
-
-  /**
-   * Verify the exchange's /wire-method.
-   */
-  OC_WIRE,
-
-  /**
-   * Verify exchange's /track/transfer method.
-   */
-  OC_WIRE_DEPOSITS,
-
-  /**
-   * Verify exchange's /track/transaction method.
-   */
-  OC_DEPOSIT_WTID,
-
-  /**
-   * Run the aggregator to execute deposits.
-   */
-  OC_RUN_AGGREGATOR,
-
-  /**
-   * Run the wirewatcher to check for incoming transactions.
-   */
-  OC_RUN_WIREWATCH,
-
-  /**
-   * Check that the fakebank has received a certain transaction.
-   */
-  OC_CHECK_BANK_TRANSFER,
-
-  /**
-   * Check that the fakebank has not received any other transactions.
-   */
-  OC_CHECK_BANK_TRANSFERS_EMPTY,
-
-  /**
-   * Refund some deposit.
-   */
-  OC_REFUND,
-
-  /**
-   * Revoke some denomination key.
-   */
-  OC_REVOKE,
-
-  /**
-   * Payback some coin.
-   */
-  OC_PAYBACK
-
-};
-
-
-/**
- * Structure specifying details about a coin to be melted.
- * Used in a NULL-terminated array as part of command
- * specification.
- */
-struct MeltDetails
-{
-
-  /**
-   * Amount to melt (including fee).
-   */
-  const char *amount;
-
-  /**
-   * Reference to reserve_withdraw operations for coin to
-   * be used for the /refresh/melt operation.
-   */
-  const char *coin_ref;
-
-};
-
-
-/**
- * Information about a fresh coin generated by the refresh operation.
- */
-struct FreshCoin
-{
-
-  /**
-   * If @e amount is NULL, this specifies the denomination key to
-   * use.  Otherwise, this will be set (by the interpreter) to the
-   * denomination PK matching @e amount.
-   */
-  const struct TALER_EXCHANGE_DenomPublicKey *pk;
-
-  /**
-   * Set (by the interpreter) to the exchange's signature over the
-   * coin's public key.
-   */
-  struct TALER_DenominationSignature sig;
-
-  /**
-   * Set (by the interpreter) to the coin's private key.
-   */
-  struct TALER_CoinSpendPrivateKeyP coin_priv;
-
-};
-
-
-/**
- * Details for a exchange operation to execute.
- */
-struct Command
-{
-  /**
-   * Opcode of the command.
-   */
-  enum OpCode oc;
-
-  /**
-   * Label for the command, can be NULL.
-   */
-  const char *label;
-
-  /**
-   * Which response code do we expect for this command?
-   */
-  unsigned int expected_response_code;
-
-  /**
-   * Details about the command.
-   */
-  union
-  {
-
-    /**
-     * Information for a #OC_ADMIN_ADD_INCOMING command.
-     */
-    struct
-    {
-
-      /**
-       * Label to another admin_add_incoming command if we
-       * should deposit into an existing reserve, NULL if
-       * a fresh reserve should be created.
-       */
-      const char *reserve_reference;
-
-      /**
-       * String describing the amount to add to the reserve.
-       */
-      const char *amount;
-
-      /**
-       * Wire transfer subject. NULL to use public key corresponding
-       * to @e reserve_priv or @e reserve_reference.  Should only be
-       * set manually to test invalid wire transfer subjects.
-       */
-      const char *subject;
-
-      /**
-       * Sender (debit) account number.
-       */
-      uint64_t debit_account_no;
-
-      /**
-       * Receiver (credit) account number.
-       */
-      uint64_t credit_account_no;
-
-      /**
-       * Username to use for authentication.
-       */
-      const char *auth_username;
-
-      /**
-       * Password to use for authentication.
-       */
-      const char *auth_password;
-
-      /**
-       * Set (by the interpreter) to the reserve's private key
-       * we used to fill the reserve.
-       */
-      struct TALER_ReservePrivateKeyP reserve_priv;
-
-      /**
-       * Set to the API's handle during the operation.
-       */
-      struct TALER_BANK_AdminAddIncomingHandle *aih;
-
-      /**
-       * Set to the wire transfer's unique ID.
-       */
-      uint64_t serial_id;
-
-    } admin_add_incoming;
-
-    /**
-     * Information for a #OC_WITHDRAW_STATUS command.
-     */
-    struct
-    {
-
-      /**
-       * Label to the #OC_ADMIN_ADD_INCOMING command which
-       * created the reserve.
-       */
-      const char *reserve_reference;
-
-      /**
-       * Set to the API's handle during the operation.
-       */
-      struct TALER_EXCHANGE_ReserveStatusHandle *wsh;
-
-      /**
-       * Expected reserve balance.
-       */
-      const char *expected_balance;
-
-    } reserve_status;
-
-    /**
-     * Information for a #OC_WITHDRAW_SIGN command.
-     */
-    struct
-    {
-
-      /**
-       * Which reserve should we withdraw from?
-       */
-      const char *reserve_reference;
-
-      /**
-       * String describing the denomination value we should withdraw.
-       * A corresponding denomination key must exist in the exchange's
-       * offerings.  Can be NULL if @e pk is set instead.
-       */
-      const char *amount;
-
-      /**
-       * If @e amount is NULL, this specifies the denomination key to
-       * use.  Otherwise, this will be set (by the interpreter) to the
-       * denomination PK matching @e amount.
-       */
-      const struct TALER_EXCHANGE_DenomPublicKey *pk;
-
-      /**
-       * Set (by the interpreter) to the exchange's signature over the
-       * coin's public key.
-       */
-      struct TALER_DenominationSignature sig;
-
-      /**
-       * Private key material of the coin, set by the interpreter.
-       */
-      struct TALER_PlanchetSecretsP ps;
-
-      /**
-       * Withdraw handle (while operation is running).
-       */
-      struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh;
-
-    } reserve_withdraw;
-
-    /**
-     * Information for a #OC_DEPOSIT command.
-     */
-    struct
-    {
-
-      /**
-       * Amount to deposit.
-       */
-      const char *amount;
-
-      /**
-       * Reference to a reserve_withdraw operation for a coin to
-       * be used for the /deposit operation.
-       */
-      const char *coin_ref;
-
-      /**
-       * If this @e coin_ref refers to an operation that generated
-       * an array of coins, this value determines which coin to use.
-       */
-      unsigned int coin_idx;
-
-      /**
-       * JSON string describing the merchant's "wire details".
-       */
-      const char *wire_details;
-
-      /**
-       * JSON string describing what a proposal is about.
-       */
-      const char *contract_terms;
-
-      /**
-       * Relative time (to add to 'now') to compute the refund deadline.
-       * Zero for no refunds.
-       */
-      struct GNUNET_TIME_Relative refund_deadline;
-
-      /**
-       * Set (by the interpreter) to a fresh private key of the merchant,
-       * if @e refund_deadline is non-zero.
-       */
-      struct TALER_MerchantPrivateKeyP merchant_priv;
-
-      /**
-       * Deposit handle while operation is running.
-       */
-      struct TALER_EXCHANGE_DepositHandle *dh;
-
-    } deposit;
-
-    /**
-     * Information for a #OC_REFRESH_MELT command.
-     */
-    struct
-    {
-
-      /**
-       * Information about coins to be melted.
-       */
-      struct MeltDetails melted_coin;
-
-      /**
-       * Denominations of the fresh coins to withdraw.
-       */
-      const char **fresh_amounts;
-
-      /**
-       * Array of the public keys corresponding to
-       * the @e fresh_amounts, set by the interpreter.
-       */
-      const struct TALER_EXCHANGE_DenomPublicKey **fresh_pks;
-
-      /**
-       * Melt handle while operation is running.
-       */
-      struct TALER_EXCHANGE_RefreshMeltHandle *rmh;
-
-      /**
-       * Data used in the refresh operation, set by the interpreter.
-       */
-      char *refresh_data;
-
-      /**
-       * Number of bytes in @e refresh_data, set by the interpreter.
-       */
-      size_t refresh_data_length;
-
-      /**
-       * Set by the interpreter (upon completion) to the noreveal
-       * index selected by the exchange.
-       */
-      uint16_t noreveal_index;
-
-    } refresh_melt;
-
-    /**
-     * Information for a #OC_REFRESH_REVEAL command.
-     */
-    struct
-    {
-
-      /**
-       * Melt operation this is the matching reveal for.
-       */
-      const char *melt_ref;
-
-      /**
-       * Reveal handle while operation is running.
-       */
-      struct TALER_EXCHANGE_RefreshRevealHandle *rrh;
-
-      /**
-       * Number of fresh coins withdrawn, set by the interpreter.
-       * Length of the @e fresh_coins array.
-       */
-      unsigned int num_fresh_coins;
-
-      /**
-       * Information about coins withdrawn, set by the interpreter.
-       */
-      struct FreshCoin *fresh_coins;
-
-    } refresh_reveal;
-
-    /**
-     * Information for a #OC_REFRESH_LINK command.
-     */
-    struct
-    {
-
-      /**
-       * Reveal operation this is the matching link for.
-       */
-      const char *reveal_ref;
-
-      /**
-       * Link handle while operation is running.
-       */
-      struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
-
-      /**
-       * Which of the melted coins should be used for the linkage?
-       */
-      unsigned int coin_idx;
-
-    } refresh_link;
-
-    /**
-     * Information for the /wire command.
-     */
-    struct {
-
-      /**
-       * Handle to the wire request.
-       */
-      struct TALER_EXCHANGE_WireHandle *wh;
-
-      /**
-       * Format we expect to see, others will be *ignored*.
-       */
-      const char *format;
-
-      /**
-       * Expected wire fee.
-       */
-      const char *expected_fee;
-
-    } wire;
-
-    /**
-     * Information for the /track/transfer's command.
-     */
-    struct {
-
-      /**
-       * Handle to the wire deposits request.
-       */
-      struct TALER_EXCHANGE_TrackTransferHandle *wdh;
-
-      /**
-       * Reference to a command providing a WTID. If set, we use the
-       * WTID from that command.  The command can be either an
-       * #OC_DEPOSIT_WTID or an #OC_CHECK_BANK_TRANSFER.  In the
-       * case of the bank transfer, we check that the total amount
-       * claimed by the exchange matches the total amount transferred
-       * by the bank.  In the case of a /track/transaction, we check
-       * that the wire details match.
-       */
-      const char *wtid_ref;
-
-      /**
-       * WTID to use (used if @e wtid_ref is NULL).
-       */
-      struct TALER_WireTransferIdentifierRawP wtid;
-
-      /**
-       * What is the expected total amount? Only used if
-       * @e expected_response_code was #MHD_HTTP_OK.
-       */
-      const char *total_amount_expected;
-
-      /**
-       * What is the expected wire fee? Only used if
-       * @e expected_response_code was #MHD_HTTP_OK.
-       */
-      const char *wire_fee_expected;
-
-
-      /* TODO: may want to add list of deposits we expected
-         to see aggregated here in the future. */
-
-    } wire_deposits;
-
-    /**
-     * Information for the /track/transaction command.
-     */
-    struct {
-
-      /**
-       * Handle to the deposit wtid request.
-       */
-      struct TALER_EXCHANGE_TrackTransactionHandle *dwh;
-
-      /**
-       * Which /deposit operation should we obtain WTID data for?
-       */
-      const char *deposit_ref;
-
-      /**
-       * Which #OC_CHECK_BANK_TRANSFER wtid should this match? NULL for none.
-       */
-      const char *bank_transfer_ref;
-
-      /**
-       * Wire transfer identifier, set if #MHD_HTTP_OK was the response code.
-       */
-      struct TALER_WireTransferIdentifierRawP wtid;
-
-    } deposit_wtid;
-
-    struct {
-
-      /**
-       * Process for the aggregator.
-       */
-      struct GNUNET_OS_Process *aggregator_proc;
-
-      /**
-       * ID of task called whenever we get a SIGCHILD.
-       */
-      struct GNUNET_SCHEDULER_Task *child_death_task;
-
-    } run_aggregator;
-
-    struct {
-
-      /**
-       * Process for the wirewatcher.
-       */
-      struct GNUNET_OS_Process *wirewatch_proc;
-
-      /**
-       * ID of task called whenever we get a SIGCHILD.
-       */
-      struct GNUNET_SCHEDULER_Task *child_death_task;
-
-    } run_wirewatch;
-
-    struct {
-
-      /**
-       * Which amount do we expect to see transferred?
-       */
-      const char *amount;
-
-      /**
-       * Which account do we expect to be debited?
-       */
-      uint64_t account_debit;
-
-      /**
-       * Which account do we expect to be credited?
-       */
-      uint64_t account_credit;
-
-      /**
-       * Which exchange base URL is expected?
-       */
-      const char *exchange_base_url;
-
-      /**
-       * Set (!) to the wire transfer subject observed.
-       */
-      char *subject;
-
-    } check_bank_transfer;
-
-    struct {
-
-      /**
-       * Amount that should be refunded.
-       */
-      const char *amount;
-
-      /**
-       * Expected refund fee.
-       */
-      const char *fee;
-
-      /**
-       * Reference to the corresponding deposit operation.
-       * Used to obtain proposal details, merchant keys,
-       * fee structure, etc.
-       */
-      const char *deposit_ref;
-
-      /**
-       * Refund transaction identifier.
-       */
-      uint64_t rtransaction_id;
-
-      /**
-       * Handle to the refund operation (while it is ongoing).
-       */
-      struct TALER_EXCHANGE_RefundHandle *rh;
-
-    } refund;
-
-    struct {
-
-      /**
-       * Reference to a _coin's_ withdraw operation where the coin's 
denomination key
-       * is the denomination key to be revoked.
-       */
-      const char *ref;
-
-      /**
-       * Process for the revocation process.
-       */
-      struct GNUNET_OS_Process *revoke_proc;
-
-      /**
-       * ID of task called whenever we get a SIGCHILD.
-       */
-      struct GNUNET_SCHEDULER_Task *child_death_task;
-
-    } revoke;
-
-    struct {
-
-      /**
-       * Reference to the _coin's_ withdraw operation.
-       */
-      const char *ref;
-
-      /**
-       * Amount that should be paid back.
-       */
-      const char *amount;
-
-      /**
-       * Handle to the ongoing /payback operation.
-       */
-      struct TALER_EXCHANGE_PaybackHandle *ph;
-
-    } payback;
-
-
-  } details;
-
-};
-
-
-/**
- * State of the interpreter loop.
- */
-struct InterpreterState
-{
-  /**
-   * Keys from the exchange.
-   */
-  const struct TALER_EXCHANGE_Keys *keys;
-
-  /**
-   * Commands the interpreter will run.
-   */
-  struct Command *commands;
-
-  /**
-   * Interpreter task (if one is scheduled).
-   */
-  struct GNUNET_SCHEDULER_Task *task;
-
-  /**
-   * Instruction pointer.  Tells #interpreter_run() which
-   * instruction to run next.
-   */
-  unsigned int ip;
-
-};
-
-
-/**
- * Pipe used to communicate child death via signal.
- */
-static struct GNUNET_DISK_PipeHandle *sigpipe;
-
-
-/**
- * The testcase failed, return with an error code.
- *
- * @param is interpreter state to clean up
- */
-static void
-fail (struct InterpreterState *is)
-{
-  result = GNUNET_SYSERR;
-  GNUNET_SCHEDULER_shutdown ();
-}
-
-
-/**
- * Find a command by label.
- *
- * @param is interpreter state to search
- * @param label label to look for
- * @return NULL if command was not found
- */
-static const struct Command *
-find_command (const struct InterpreterState *is,
-              const char *label)
-{
-  unsigned int i;
-  const struct Command *cmd;
-
-  if (NULL == label)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Attempt to lookup command for empty label\n");
-    return NULL;
-  }
-  for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
-    if ( (NULL != cmd->label) &&
-         (0 == strcmp (cmd->label,
-                       label)) )
-      return cmd;
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-              "Command not found: %s\n",
-              label);
-  return NULL;
-}
-
-
-/**
- * Run the main interpreter loop that performs exchange operations.
- *
- * @param cls contains the `struct InterpreterState`
- */
-static void
-interpreter_run (void *cls);
-
-
-/**
- * Run the next command with the interpreter.
- *
- * @param is current interpeter state.
- */
-static void
-next_command (struct InterpreterState *is)
-{
-  if (GNUNET_SYSERR == result)
-    return; /* ignore, we already failed! */
-  is->ip++;
-  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
-                                       is);
-}
-
-
-/**
- * Function called upon completion of our /admin/add/incoming request.
- *
- * @param cls closure with the interpreter state
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
- *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param serial_id unique ID of the wire transfer
- * @param full_response full response from the exchange (for logging, in case 
of errors)
- */
-static void
-add_incoming_cb (void *cls,
-                 unsigned int http_status,
-                enum TALER_ErrorCode ec,
-                 uint64_t serial_id,
-                 const json_t *full_response)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-
-  cmd->details.admin_add_incoming.aih = NULL;
-  cmd->details.admin_add_incoming.serial_id = serial_id;
-  if (MHD_HTTP_OK != http_status)
-  {
-    GNUNET_break (0);
-    fail (is);
-    return;
-  }
-  next_command (is);
-}
-
-
-/**
- * Check if the given historic event @a h corresponds to the given
- * command @a cmd.
- *
- * @param h event in history
- * @param cmd an #OC_ADMIN_ADD_INCOMING command
- * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not
- */
-static int
-compare_admin_add_incoming_history (const struct TALER_EXCHANGE_ReserveHistory 
*h,
-                                    const struct Command *cmd)
-{
-  struct TALER_Amount amount;
-
-  if (TALER_EXCHANGE_RTT_DEPOSIT != h->type)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_string_to_amount 
(cmd->details.admin_add_incoming.amount,
-                                         &amount));
-  if (0 != TALER_amount_cmp (&amount,
-                             &h->amount))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Check if the given historic event @a h corresponds to the given
- * command @a cmd.
- *
- * @param h event in history
- * @param cmd an #OC_WITHDRAW_SIGN command
- * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not
- */
-static int
-compare_reserve_withdraw_history (const struct TALER_EXCHANGE_ReserveHistory 
*h,
-                                  const struct Command *cmd)
-{
-  struct TALER_Amount amount;
-  struct TALER_Amount amount_with_fee;
-
-  if (TALER_EXCHANGE_RTT_WITHDRAWAL != h->type)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_string_to_amount (cmd->details.reserve_withdraw.amount,
-                                         &amount));
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_add (&amount_with_fee,
-                                   &amount,
-                                   
&cmd->details.reserve_withdraw.pk->fee_withdraw));
-  if (0 != TALER_amount_cmp (&amount_with_fee,
-                             &h->amount))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Check if the given historic event @a h corresponds to the given
- * command @a cmd.
- *
- * @param h event in history
- * @param cmd an #OC_WITHDRAW_SIGN command
- * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not
- */
-static int
-compare_reserve_payback_history (const struct TALER_EXCHANGE_ReserveHistory *h,
-                                 const struct Command *cmd)
-{
-  struct TALER_Amount amount;
-
-  if (TALER_EXCHANGE_RTT_PAYBACK != h->type)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_string_to_amount (cmd->details.payback.amount,
-                                         &amount));
-  if (0 != TALER_amount_cmp (&amount,
-                             &h->amount))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called with the result of a /reserve/status request.
- *
- * @param cls closure with the interpreter state
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
- *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param[in] json original response in JSON format (useful only for 
diagnostics)
- * @param balance current balance in the reserve, NULL on error
- * @param history_length number of entries in the transaction history, 0 on 
error
- * @param history detailed transaction history, NULL on error
- */
-static void
-reserve_status_cb (void *cls,
-                   unsigned int http_status,
-                  enum TALER_ErrorCode ec,
-                   const json_t *json,
-                   const struct TALER_Amount *balance,
-                   unsigned int history_length,
-                   const struct TALER_EXCHANGE_ReserveHistory *history)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-  struct Command *rel;
-  const struct Command *xrel;
-  unsigned int i;
-  unsigned int j;
-  struct TALER_Amount amount;
-
-  cmd->details.reserve_status.wsh = NULL;
-  if (cmd->expected_response_code != http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
-                http_status,
-                cmd->label);
-    GNUNET_break (0);
-    json_dumpf (json, stderr, 0);
-    fail (is);
-    return;
-  }
-  switch (http_status)
-  {
-  case MHD_HTTP_OK:
-    /* FIXME: note that history events may come in a different
-       order than the commands. However, for now this works... */
-    j = 0;
-    for (i=0;i<is->ip;i++)
-    {
-      switch ((rel = &is->commands[i])->oc)
-      {
-      case OC_ADMIN_ADD_INCOMING:
-        if ( ( (NULL != rel->label) &&
-               (0 == strcmp (cmd->details.reserve_status.reserve_reference,
-                             rel->label) ) ) ||
-             ( (NULL != rel->details.admin_add_incoming.reserve_reference) &&
-               (0 == strcmp (cmd->details.reserve_status.reserve_reference,
-                             
rel->details.admin_add_incoming.reserve_reference) ) ) )
-        {
-          if ( (j >= history_length) ||
-               (GNUNET_OK !=
-                compare_admin_add_incoming_history (&history[j],
-                                                    rel)) )
-          {
-            GNUNET_break (0);
-            fail (is);
-            return;
-          }
-          j++;
-        }
-        break;
-      case OC_WITHDRAW_SIGN:
-        if (0 == strcmp (cmd->details.reserve_status.reserve_reference,
-                         rel->details.reserve_withdraw.reserve_reference))
-        {
-          if ( (j >= history_length) ||
-               (GNUNET_OK !=
-                compare_reserve_withdraw_history (&history[j],
-                                                  rel)) )
-          {
-            GNUNET_break (0);
-            fail (is);
-            return;
-          }
-          j++;
-        }
-        break;
-      case OC_PAYBACK:
-        xrel = find_command (is,
-                             rel->details.payback.ref);
-        GNUNET_assert (NULL != xrel);
-        if (0 == strcmp (cmd->details.reserve_status.reserve_reference,
-                         xrel->details.reserve_withdraw.reserve_reference))
-        {
-          if ( (j >= history_length) ||
-               (GNUNET_OK !=
-                compare_reserve_payback_history (&history[j],
-                                                 rel)) )
-          {
-            GNUNET_break (0);
-            fail (is);
-            return;
-          }
-          j++;
-        }
-        break;
-      default:
-        /* unreleated, just skip */
-        break;
-      }
-    }
-    if (j != history_length)
-    {
-      GNUNET_break (0);
-      fail (is);
-      return;
-    }
-    if (NULL != cmd->details.reserve_status.expected_balance)
-    {
-      GNUNET_assert (GNUNET_OK ==
-                     TALER_string_to_amount 
(cmd->details.reserve_status.expected_balance,
-                                             &amount));
-      if (0 != TALER_amount_cmp (&amount,
-                                 balance))
-      {
-        GNUNET_break (0);
-        fail (is);
-        return;
-      }
-    }
-    break;
-  default:
-    /* Unsupported status code (by test harness) */
-    GNUNET_break (0);
-    break;
-  }
-  next_command (is);
-}
-
-
-/**
- * Function called upon completion of our /reserve/withdraw request.
- *
- * @param cls closure with the interpreter state
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
- *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param sig signature over the coin, NULL on error
- * @param full_response full response from the exchange (for logging, in case 
of errors)
- */
-static void
-reserve_withdraw_cb (void *cls,
-                     unsigned int http_status,
-                    enum TALER_ErrorCode ec,
-                     const struct TALER_DenominationSignature *sig,
-                     const json_t *full_response)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-
-  cmd->details.reserve_withdraw.wsh = NULL;
-  if (cmd->expected_response_code != http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
-                http_status,
-                cmd->label);
-    json_dumpf (full_response, stderr, 0);
-    GNUNET_break (0);
-    fail (is);
-    return;
-  }
-  switch (http_status)
-  {
-  case MHD_HTTP_OK:
-    if (NULL == sig)
-    {
-      GNUNET_break (0);
-      fail (is);
-      return;
-    }
-    cmd->details.reserve_withdraw.sig.rsa_signature
-      = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature);
-    break;
-  case MHD_HTTP_FORBIDDEN:
-    /* nothing to check */
-    break;
-  case MHD_HTTP_NOT_FOUND:
-    /* nothing to check */
-    break;
-  default:
-    /* Unsupported status code (by test harness) */
-    GNUNET_break (0);
-    break;
-  }
-  next_command (is);
-}
-
-
-/**
- * Function called with the result of a /deposit operation.
- *
- * @param cls closure with the interpreter state
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
deposit;
- *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param exchange_pub public key the exchange used for signing
- * @param obj the received JSON reply, should be kept as proof (and, in case 
of errors,
- *            be forwarded to the customer)
- */
-static void
-deposit_cb (void *cls,
-            unsigned int http_status,
-           enum TALER_ErrorCode ec,
-            const struct TALER_ExchangePublicKeyP *exchange_pub,
-            const json_t *obj)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-
-  cmd->details.deposit.dh = NULL;
-  if (cmd->expected_response_code != http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
-                http_status,
-                cmd->label);
-    json_dumpf (obj, stderr, 0);
-    fail (is);
-    return;
-  }
-  next_command (is);
-}
-
-
-/**
- * Function called with the result of the /refresh/melt operation.
- *
- * @param cls closure with the interpreter state
- * @param http_status HTTP response code, never #MHD_HTTP_OK (200) as for 
successful intermediate response this callback is skipped.
- *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param noreveal_index choice by the exchange in the cut-and-choose protocol,
- *                    UINT16_MAX on error
- * @param exchange_pub public key the exchange used for signing
- * @param full_response full response from the exchange (for logging, in case 
of errors)
- */
-static void
-melt_cb (void *cls,
-         unsigned int http_status,
-        enum TALER_ErrorCode ec,
-         uint32_t noreveal_index,
-         const struct TALER_ExchangePublicKeyP *exchange_pub,
-         const json_t *full_response)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-
-  cmd->details.refresh_melt.rmh = NULL;
-  if (cmd->expected_response_code != http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
-                http_status,
-                cmd->label);
-    json_dumpf (full_response, stderr, 0);
-    fail (is);
-    return;
-  }
-  cmd->details.refresh_melt.noreveal_index = noreveal_index;
-  next_command (is);
-}
-
-
-/**
- * Function called with the result of the /refresh/reveal operation.
- *
- * @param cls closure with the interpreter state
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
- *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param num_coins number of fresh coins created, length of the @a sigs and 
@a coin_privs arrays, 0 if the operation failed
- * @param coin_privs array of @a num_coins private keys for the coins that 
were created, NULL on error
- * @param sigs array of signature over @a num_coins coins, NULL on error
- * @param full_response full response from the exchange (for logging, in case 
of errors)
- */
-static void
-reveal_cb (void *cls,
-           unsigned int http_status,
-          enum TALER_ErrorCode ec,
-           unsigned int num_coins,
-           const struct TALER_CoinSpendPrivateKeyP *coin_privs,
-           const struct TALER_DenominationSignature *sigs,
-           const json_t *full_response)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-  const struct Command *ref;
-  unsigned int i;
-
-  cmd->details.refresh_reveal.rrh = NULL;
-  if (cmd->expected_response_code != http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
-                http_status,
-                cmd->label);
-    json_dumpf (full_response, stderr, 0);
-    fail (is);
-    return;
-  }
-  ref = find_command (is,
-                      cmd->details.refresh_reveal.melt_ref);
-  GNUNET_assert (NULL != ref);
-  cmd->details.refresh_reveal.num_fresh_coins = num_coins;
-  switch (http_status)
-  {
-  case MHD_HTTP_OK:
-    cmd->details.refresh_reveal.fresh_coins
-      = GNUNET_new_array (num_coins,
-                          struct FreshCoin);
-    for (i=0;i<num_coins;i++)
-    {
-      struct FreshCoin *fc = &cmd->details.refresh_reveal.fresh_coins[i];
-
-      fc->pk = ref->details.refresh_melt.fresh_pks[i];
-      fc->coin_priv = coin_privs[i];
-      fc->sig.rsa_signature
-        = GNUNET_CRYPTO_rsa_signature_dup (sigs[i].rsa_signature);
-    }
-    break;
-  default:
-    break;
-  }
-  next_command (is);
-}
-
-
-/**
- * Function called with the result of a /refresh/link operation.
- *
- * @param cls closure with the interpreter state
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
- *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param num_coins number of fresh coins created, length of the @a sigs and 
@a coin_privs arrays, 0 if the operation failed
- * @param coin_privs array of @a num_coins private keys for the coins that 
were created, NULL on error
- * @param sigs array of signature over @a num_coins coins, NULL on error
- * @param pubs array of public keys for the @a sigs, NULL on error
- * @param full_response full response from the exchange (for logging, in case 
of errors)
- */
-static void
-link_cb (void *cls,
-         unsigned int http_status,
-        enum TALER_ErrorCode ec,
-         unsigned int num_coins,
-         const struct TALER_CoinSpendPrivateKeyP *coin_privs,
-         const struct TALER_DenominationSignature *sigs,
-         const struct TALER_DenominationPublicKey *pubs,
-         const json_t *full_response)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-  const struct Command *ref;
-  unsigned int found;
-
-  cmd->details.refresh_link.rlh = NULL;
-  if (cmd->expected_response_code != http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
-                http_status,
-                cmd->label);
-    json_dumpf (full_response, stderr, 0);
-    fail (is);
-    return;
-  }
-  ref = find_command (is,
-                      cmd->details.refresh_link.reveal_ref);
-  GNUNET_assert (NULL != ref);
-  switch (http_status)
-  {
-  case MHD_HTTP_OK:
-    /* check that number of coins returned matches */
-    if (num_coins != ref->details.refresh_reveal.num_fresh_coins)
-    {
-      GNUNET_break (0);
-      fail (is);
-      return;
-    }
-    /* check that the coins match */
-    for (unsigned int i=0;i<num_coins;i++)
-      for (unsigned int j=i+1;j<num_coins;j++)
-       if (0 == memcmp (&coin_privs[i],
-                        &coin_privs[j],
-                        sizeof (struct TALER_CoinSpendPrivateKeyP)))
-         GNUNET_break (0);
-    /* Note: coins might be legitimately permutated in here... */
-    found = 0;
-    for (unsigned int i=0;i<num_coins;i++)
-      for (unsigned int j=0;j<num_coins;j++)
-      {
-       const struct FreshCoin *fc;
-
-       fc = &ref->details.refresh_reveal.fresh_coins[j];
-       if ( (0 == memcmp (&coin_privs[i],
-                          &fc->coin_priv,
-                          sizeof (struct TALER_CoinSpendPrivateKeyP))) &&
-            (0 == GNUNET_CRYPTO_rsa_signature_cmp (fc->sig.rsa_signature,
-                                                   sigs[i].rsa_signature)) &&
-            (0 == GNUNET_CRYPTO_rsa_public_key_cmp (fc->pk->key.rsa_public_key,
-                                                    pubs[i].rsa_public_key)) )
-       {
-         found++;
-         break;
-       }
-      }
-    if (found != num_coins)
-    {
-      fprintf (stderr,
-              "Only %u/%u coins match expectations\n",
-              found,
-              num_coins);
-      GNUNET_break (0);
-      fail (is);
-      return;
-    }
-    break;
-  default:
-    break;
-  }
-  next_command (is);
-}
-
-
-/**
- * Task triggered whenever we receive a SIGCHLD (child
- * process died).
- *
- * @param cls closure, NULL if we need to self-restart
- */
-static void
-maint_child_death (void *cls)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-  const struct GNUNET_DISK_FileHandle *pr;
-  char c[16];
-
-  switch (cmd->oc) {
-  case OC_RUN_AGGREGATOR:
-    cmd->details.run_aggregator.child_death_task = NULL;
-    pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
-    GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
-    GNUNET_OS_process_wait (cmd->details.run_aggregator.aggregator_proc);
-    GNUNET_OS_process_destroy (cmd->details.run_aggregator.aggregator_proc);
-    cmd->details.run_aggregator.aggregator_proc = NULL;
-    break;
-  case OC_RUN_WIREWATCH:
-    cmd->details.run_wirewatch.child_death_task = NULL;
-    pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
-    GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
-    GNUNET_OS_process_wait (cmd->details.run_wirewatch.wirewatch_proc);
-    GNUNET_OS_process_destroy (cmd->details.run_wirewatch.wirewatch_proc);
-    cmd->details.run_wirewatch.wirewatch_proc = NULL;
-    break;
-  case OC_REVOKE:
-    cmd->details.revoke.child_death_task = NULL;
-    pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
-    GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
-    GNUNET_OS_process_wait (cmd->details.revoke.revoke_proc);
-    GNUNET_OS_process_destroy (cmd->details.revoke.revoke_proc);
-    cmd->details.revoke.revoke_proc = NULL;
-    /* trigger reload of denomination key information */
-    GNUNET_break (0 ==
-                  GNUNET_OS_process_kill (exchanged,
-                                          SIGUSR1));
-    sleep (5); /* make sure signal was received and processed */
-    break;
-  default:
-    GNUNET_break (0);
-    fail (is);
-    return;
-  }
-  next_command (is);
-}
-
-
-/**
- * Find denomination key matching the given amount.
- *
- * @param keys array of keys to search
- * @param amount coin value to look for
- * @return NULL if no matching key was found
- */
-static const struct TALER_EXCHANGE_DenomPublicKey *
-find_pk (const struct TALER_EXCHANGE_Keys *keys,
-         const struct TALER_Amount *amount)
-{
-  struct GNUNET_TIME_Absolute now;
-  struct TALER_EXCHANGE_DenomPublicKey *pk;
-  char *str;
-
-  now = GNUNET_TIME_absolute_get ();
-  for (unsigned int i=0;i<keys->num_denom_keys;i++)
-  {
-    pk = &keys->denom_keys[i];
-    if ( (0 == TALER_amount_cmp (amount,
-                                 &pk->value)) &&
-         (now.abs_value_us >= pk->valid_from.abs_value_us) &&
-         (now.abs_value_us < pk->withdraw_valid_until.abs_value_us) )
-      return pk;
-  }
-  /* do 2nd pass to check if expiration times are to blame for failure */
-  str = TALER_amount_to_string (amount);
-  for (unsigned int i=0;i<keys->num_denom_keys;i++)
-  {
-    pk = &keys->denom_keys[i];
-    if ( (0 == TALER_amount_cmp (amount,
-                                 &pk->value)) &&
-         ( (now.abs_value_us < pk->valid_from.abs_value_us) ||
-           (now.abs_value_us > pk->withdraw_valid_until.abs_value_us) ) )
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Have denomination key for `%s', but with wrong expiration 
range %llu vs [%llu,%llu)\n",
-                  str,
-                  (unsigned long long) now.abs_value_us,
-                  (unsigned long long) pk->valid_from.abs_value_us,
-                  (unsigned long long) pk->withdraw_valid_until.abs_value_us);
-      GNUNET_free (str);
-      return NULL;
-    }
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-              "No denomination key for amount %s found\n",
-              str);
-  GNUNET_free (str);
-  return NULL;
-}
-
-
-#if LEGACY
-/* Tests the *old* /wire API, the _modern_ testcase was adapted,
-   but little point in right now adapting the old testcase */
-/**
- * Function called with information about the wire fees
- * for each wire method.
- *
- * @param cls closure
- * @param wire_method name of the wire method (i.e. "ebics")
- * @param fees fee structure for this method
- */
-static void
-check_fee_cb (void *cls,
-              const char *wire_method,
-              const struct TALER_EXCHANGE_WireAggregateFees *fees)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-  struct TALER_Amount expected_amount;
-
-  GNUNET_break ( (0 == strcasecmp ("x-taler-bank",
-                                   wire_method)) ||
-                 (0 == strcasecmp ("ebics",
-                                   wire_method)) );
-  if (GNUNET_OK !=
-      TALER_string_to_amount (cmd->details.wire.expected_fee,
-                              &expected_amount))
-  {
-    GNUNET_break (0);
-    fail (is);
-    return;
-  }
-  while (NULL != fees)
-  {
-    if (0 != TALER_amount_cmp (&fees->wire_fee,
-                               &expected_amount))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Wire fee missmatch to command %s\n",
-                  cmd->label);
-      fail (is);
-      return;
-    }
-    fees = fees->next;
-  }
-}
-#endif
-
-
-/**
- * Callbacks called with the result(s) of a
- * wire format inquiry request to the exchange.
- *
- * @param cls closure with the interpreter state
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
request;
- *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param accounts_len length of the @a accounts array
- * @param accounts list of wire accounts of the exchange, NULL on error
- */
-static void
-wire_cb (void *cls,
-         unsigned int http_status,
-        enum TALER_ErrorCode ec,
-         unsigned int accounts_len,
-         const struct TALER_EXCHANGE_WireAccount *accounts)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-
-  cmd->details.wire.wh = NULL;
-  if (cmd->expected_response_code != http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
-                http_status,
-                cmd->label);
-#if LEGACY
-    json_dumpf (obj, stderr, 0);
-#endif
-    fail (is);
-    return;
-  }
-  switch (http_status)
-  {
-  case MHD_HTTP_OK:
-    {
-#if LEGACY
-      json_t *method;
-
-      method = json_object_get (obj,
-                                cmd->details.wire.format);
-      if (NULL == method)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Expected method `%s' not included in response to command 
%s\n",
-                    cmd->details.wire.format,
-                    cmd->label);
-        json_dumpf (obj, stderr, 0);
-        fail (is);
-        return;
-      }
-      if (GNUNET_OK !=
-          TALER_EXCHANGE_wire_get_fees (&TALER_EXCHANGE_get_keys 
(exchange)->master_pub,
-                                        obj,
-                                        &check_fee_cb,
-                                        is))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Wire fee extraction in command %s failed\n",
-                    cmd->label);
-        json_dumpf (obj, stderr, 0);
-        fail (is);
-        return;
-      }
-#endif
-    }
-    break;
-  default:
-    break;
-  }
-  next_command (is);
-}
-
-
-/**
- * Function called with detailed wire transfer data, including all
- * of the coin transactions that were combined into the wire transfer.
- *
- * @param cls closure
- * @param http_status HTTP status code we got, 0 on exchange protocol violation
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param exchange_pub public key the exchange used for signing
- * @param json original json reply (may include signatures, those have then 
been
- *        validated already)
- * @param h_wire hash of the wire transfer address the transfer went to, or 
NULL on error
- * @param execution_time time when the exchange claims to have performed the 
wire transfer
- * @param total_amount total amount of the wire transfer, or NULL if the 
exchange could
- *             not provide any @a wtid (set only if @a http_status is 
#MHD_HTTP_OK)
- * @param wire_fee wire fee that was charged by the exchange
- * @param details_length length of the @a details array
- * @param details array with details about the combined transactions
- */
-static void
-wire_deposits_cb (void *cls,
-                  unsigned int http_status,
-                 enum TALER_ErrorCode ec,
-                  const struct TALER_ExchangePublicKeyP *exchange_pub,
-                  const json_t *json,
-                  const struct GNUNET_HashCode *h_wire,
-                  struct GNUNET_TIME_Absolute execution_time,
-                  const struct TALER_Amount *total_amount,
-                  const struct TALER_Amount *wire_fee,
-                  unsigned int details_length,
-                  const struct TALER_TrackTransferDetails *details)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-  const struct Command *ref;
-  struct TALER_Amount expected_amount;
-
-  cmd->details.wire_deposits.wdh = NULL;
-  if (cmd->expected_response_code != http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
-                http_status,
-                cmd->label);
-    json_dumpf (json, stderr, 0);
-    fail (is);
-    return;
-  }
-  switch (http_status)
-  {
-  case MHD_HTTP_OK:
-    if (GNUNET_OK !=
-        TALER_string_to_amount 
(cmd->details.wire_deposits.total_amount_expected,
-                                &expected_amount))
-    {
-      GNUNET_break (0);
-      fail (is);
-      return;
-    }
-    if (0 != TALER_amount_cmp (total_amount,
-                               &expected_amount))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Total amount missmatch to command %s\n",
-                  cmd->label);
-      json_dumpf (json, stderr, 0);
-      fail (is);
-      return;
-    }
-    if (GNUNET_OK !=
-        TALER_string_to_amount (cmd->details.wire_deposits.wire_fee_expected,
-                                &expected_amount))
-    {
-      GNUNET_break (0);
-      fail (is);
-      return;
-    }
-    if (0 != TALER_amount_cmp (wire_fee,
-                               &expected_amount))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Wire fee missmatch to command %s\n",
-                  cmd->label);
-      json_dumpf (json, stderr, 0);
-      fail (is);
-      return;
-    }
-    ref = find_command (is,
-                        cmd->details.wire_deposits.wtid_ref);
-    GNUNET_assert (NULL != ref);
-    switch (ref->oc)
-    {
-    case OC_DEPOSIT_WTID:
-      if (NULL != ref->details.deposit_wtid.deposit_ref)
-      {
-        const struct Command *dep;
-        struct GNUNET_HashCode hw;
-        json_t *wire;
-
-        dep = find_command (is,
-                            ref->details.deposit_wtid.deposit_ref);
-        GNUNET_assert (NULL != dep);
-        wire = json_loads (dep->details.deposit.wire_details,
-                           JSON_REJECT_DUPLICATES,
-                           NULL);
-        GNUNET_assert (GNUNET_OK ==
-                       TALER_JSON_wire_signature_hash (wire,
-                                                       &hw));
-        json_decref (wire);
-        if (0 != memcmp (&hw,
-                         h_wire,
-                         sizeof (struct GNUNET_HashCode)))
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                      "Wire hash missmatch to command %s\n",
-                      cmd->label);
-          json_dumpf (json, stderr, 0);
-          fail (is);
-          return;
-        }
-      }
-      break;
-    case OC_CHECK_BANK_TRANSFER:
-      if (GNUNET_OK !=
-          TALER_string_to_amount (ref->details.check_bank_transfer.amount,
-                                  &expected_amount))
-      {
-        GNUNET_break (0);
-        fail (is);
-        return;
-      }
-      if (0 != TALER_amount_cmp (total_amount,
-                                 &expected_amount))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Total amount missmatch to command %s\n",
-                    cmd->label);
-        json_dumpf (json, stderr, 0);
-        fail (is);
-        return;
-      }
-      break;
-    default:
-      GNUNET_break (0);
-      fail (is);
-      return;
-    }
-    break;
-  default:
-    break;
-  }
-  next_command (is);
-}
-
-
-/**
- * Function called with detailed wire transfer data.
- *
- * @param cls closure
- * @param http_status HTTP status code we got, 0 on exchange protocol violation
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param exchange_pub public key the exchange used for signing
- * @param json original json reply (may include signatures, those have then 
been
- *        validated already)
- * @param wtid wire transfer identifier used by the exchange, NULL if exchange 
did not
- *                  yet execute the transaction
- * @param execution_time actual or planned execution time for the wire transfer
- * @param coin_contribution contribution to the @a total_amount of the 
deposited coin (may be NULL)
- * @param total_amount total amount of the wire transfer, or NULL if the 
exchange could
- *             not provide any @a wtid (set only if @a http_status is 
#MHD_HTTP_OK)
- */
-static void
-deposit_wtid_cb (void *cls,
-                 unsigned int http_status,
-                enum TALER_ErrorCode ec,
-                 const struct TALER_ExchangePublicKeyP *exchange_pub,
-                 const json_t *json,
-                 const struct TALER_WireTransferIdentifierRawP *wtid,
-                 struct GNUNET_TIME_Absolute execution_time,
-                 const struct TALER_Amount *coin_contribution)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-
-  cmd->details.deposit_wtid.dwh = NULL;
-  if (cmd->expected_response_code != http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
-                http_status,
-                cmd->label);
-    json_dumpf (json, stderr, 0);
-    fail (is);
-    return;
-  }
-  switch (http_status)
-  {
-  case MHD_HTTP_OK:
-    cmd->details.deposit_wtid.wtid = *wtid;
-    if (NULL != cmd->details.deposit_wtid.bank_transfer_ref)
-    {
-      const struct Command *ref;
-      char *ws;
-
-      ws = GNUNET_STRINGS_data_to_string_alloc (wtid,
-                                                sizeof (*wtid));
-
-
-      ref = find_command (is,
-                          cmd->details.deposit_wtid.bank_transfer_ref);
-      GNUNET_assert (NULL != ref);
-      if (0 != strcmp (ws,
-                       ref->details.check_bank_transfer.subject))
-      {
-        GNUNET_break (0);
-        GNUNET_free (ws);
-        fail (is);
-        return;
-      }
-      GNUNET_free (ws);
-    }
-    break;
-  case MHD_HTTP_ACCEPTED:
-    /* allowed, nothing to check here */
-    break;
-  case MHD_HTTP_NOT_FOUND:
-    /* allowed, nothing to check here */
-    break;
-  default:
-    GNUNET_break (0);
-    break;
-  }
-  next_command (is);
-}
-
-
-/**
- * Check the result for the refund request.
- *
- * @param cls closure
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
deposit;
- *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param exchange_pub public key the exchange used for signing @a obj
- * @param obj the received JSON reply, should be kept as proof (and, in 
particular,
- *            be forwarded to the customer)
- */
-static void
-refund_cb (void *cls,
-           unsigned int http_status,
-          enum TALER_ErrorCode ec,
-           const struct TALER_ExchangePublicKeyP *exchange_pub,
-           const json_t *obj)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-
-  cmd->details.refund.rh = NULL;
-  if (cmd->expected_response_code != http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
-                http_status,
-                cmd->label);
-    json_dumpf (obj, stderr, 0);
-    fail (is);
-    return;
-  }
-  switch (http_status)
-  {
-  case MHD_HTTP_OK:
-    break;
-  default:
-    break;
-  }
-  next_command (is);
-}
-
-
-
-/**
- * Check the result of the payback request.
- *
- * @param cls closure
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
- *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param amount amount the exchange will wire back for this coin
- * @param timestamp what time did the exchange receive the /payback request
- * @param reserve_pub public key of the reserve receiving the payback
- * @param full_response full response from the exchange (for logging, in case 
of errors)
- */
-static void
-payback_cb (void *cls,
-            unsigned int http_status,
-            enum TALER_ErrorCode ec,
-            const struct TALER_Amount *amount,
-            struct GNUNET_TIME_Absolute timestamp,
-            const struct TALER_ReservePublicKeyP *reserve_pub,
-            const json_t *full_response)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-  const struct Command *withdraw;
-  const struct Command *reserve;
-  struct TALER_Amount expected_amount;
-  struct TALER_ReservePublicKeyP rp;
-
-  cmd->details.payback.ph = NULL;
-  if (cmd->expected_response_code != http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
-                http_status,
-                cmd->label);
-    json_dumpf (full_response, stderr, 0);
-    fail (is);
-    return;
-  }
-  withdraw = find_command (is,
-                           cmd->details.payback.ref);
-  GNUNET_assert (NULL != withdraw);
-  reserve = find_command (is,
-                          
withdraw->details.reserve_withdraw.reserve_reference);
-  GNUNET_assert (NULL != reserve);
-  GNUNET_CRYPTO_eddsa_key_get_public 
(&reserve->details.admin_add_incoming.reserve_priv.eddsa_priv,
-                                      &rp.eddsa_pub);
-  switch (http_status)
-  {
-  case MHD_HTTP_OK:
-    if (GNUNET_OK !=
-        TALER_string_to_amount (cmd->details.payback.amount,
-                                &expected_amount))
-    {
-      GNUNET_break (0);
-      fail (is);
-      return;
-    }
-    if (0 != TALER_amount_cmp (amount,
-                               &expected_amount))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Total amount missmatch to command %s\n",
-                  cmd->label);
-      json_dumpf (full_response, stderr, 0);
-      fail (is);
-      return;
-    }
-    if (0 != memcmp (reserve_pub,
-                     &rp,
-                     sizeof (rp)))
-    {
-      GNUNET_break (0);
-      fail (is);
-      return;
-    }
-    break;
-  default:
-    break;
-  }
-  next_command (is);
-}
-
-
-/**
- * Given a command that is used to withdraw coins,
- * extract the corresponding public key of the coin.
- *
- * @param coin command relating to coin withdrawal or refresh
- * @param idx index to use if we got multiple coins from the @a coin command
- * @param[out] coin_pub where to store the public key of the coin
- */
-static void
-get_public_key_from_coin_command (const struct Command *coin,
-                                  unsigned int idx,
-                                  struct TALER_CoinSpendPublicKeyP *coin_pub)
-{
-  switch (coin->oc)
-  {
-  case OC_WITHDRAW_SIGN:
-    GNUNET_CRYPTO_eddsa_key_get_public 
(&coin->details.reserve_withdraw.ps.coin_priv.eddsa_priv,
-                                        &coin_pub->eddsa_pub);
-    break;
-  case OC_REFRESH_REVEAL:
-    {
-      const struct FreshCoin *fc;
-
-      GNUNET_assert (idx < coin->details.refresh_reveal.num_fresh_coins);
-      fc = &coin->details.refresh_reveal.fresh_coins[idx];
-
-      GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv,
-                                          &coin_pub->eddsa_pub);
-    }
-    break;
-  default:
-    GNUNET_assert (0);
-  }
-}
-
-
-/**
- * Run the main interpreter loop that performs exchange operations.
- *
- * @param cls contains the `struct InterpreterState`
- */
-static void
-interpreter_run (void *cls)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-  const struct Command *ref;
-  struct TALER_ReservePublicKeyP reserve_pub;
-  struct TALER_Amount amount;
-  const struct GNUNET_SCHEDULER_TaskContext *tc;
-
-  is->task = NULL;
-  tc = GNUNET_SCHEDULER_get_task_context ();
-  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
-  {
-    fprintf (stderr,
-             "Test aborted by shutdown request\n");
-    fail (is);
-    return;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Running command `%s'\n",
-              cmd->label);
-  switch (cmd->oc)
-  {
-  case OC_END:
-    result = GNUNET_OK;
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  case OC_ADMIN_ADD_INCOMING:
-    {
-      char *subject;
-      struct TALER_BANK_AuthenticationData auth;
-
-      if (NULL !=
-          cmd->details.admin_add_incoming.subject)
-      {
-        subject = GNUNET_strdup (cmd->details.admin_add_incoming.subject);
-      }
-      else
-      {
-        /* Use reserve public key as subject */
-        if (NULL !=
-            cmd->details.admin_add_incoming.reserve_reference)
-        {
-          ref = find_command (is,
-                              
cmd->details.admin_add_incoming.reserve_reference);
-          GNUNET_assert (NULL != ref);
-          GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
-          cmd->details.admin_add_incoming.reserve_priv
-            = ref->details.admin_add_incoming.reserve_priv;
-        }
-        else
-        {
-          struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
-
-          priv = GNUNET_CRYPTO_eddsa_key_create ();
-          cmd->details.admin_add_incoming.reserve_priv.eddsa_priv = *priv;
-          GNUNET_free (priv);
-        }
-        GNUNET_CRYPTO_eddsa_key_get_public 
(&cmd->details.admin_add_incoming.reserve_priv.eddsa_priv,
-                                            &reserve_pub.eddsa_pub);
-        subject = GNUNET_STRINGS_data_to_string_alloc (&reserve_pub,
-                                                       sizeof (reserve_pub));
-      }
-      if (GNUNET_OK !=
-          TALER_string_to_amount (cmd->details.admin_add_incoming.amount,
-                                  &amount))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Failed to parse amount `%s' at %u\n",
-                    cmd->details.admin_add_incoming.amount,
-                    is->ip);
-        fail (is);
-        return;
-      }
-      auth.method = TALER_BANK_AUTH_BASIC;
-      auth.details.basic.username = (char *) 
cmd->details.admin_add_incoming.auth_username;
-      auth.details.basic.password = (char *) 
cmd->details.admin_add_incoming.auth_password;
-      cmd->details.admin_add_incoming.aih
-        = TALER_BANK_admin_add_incoming (ctx,
-                                         "http://localhost:8082/";, /* bank URL 
*/
-                                         &auth,
-                                         "http://localhost:8081/";, /* exchange 
URL */
-                                         subject,
-                                         &amount,
-                                         
cmd->details.admin_add_incoming.debit_account_no,
-                                         
cmd->details.admin_add_incoming.credit_account_no,
-                                         &add_incoming_cb,
-                                         is);
-      GNUNET_free (subject);
-      if (NULL == cmd->details.admin_add_incoming.aih)
-      {
-        GNUNET_break (0);
-        fail (is);
-        return;
-      }
-    }
-    return;
-  case OC_WITHDRAW_STATUS:
-    GNUNET_assert (NULL !=
-                   cmd->details.reserve_status.reserve_reference);
-    ref = find_command (is,
-                        cmd->details.reserve_status.reserve_reference);
-    GNUNET_assert (NULL != ref);
-    GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
-    GNUNET_CRYPTO_eddsa_key_get_public 
(&ref->details.admin_add_incoming.reserve_priv.eddsa_priv,
-                                        &reserve_pub.eddsa_pub);
-    cmd->details.reserve_status.wsh
-      = TALER_EXCHANGE_reserve_status (exchange,
-                                       &reserve_pub,
-                                       &reserve_status_cb,
-                                       is);
-    return;
-  case OC_WITHDRAW_SIGN:
-    GNUNET_assert (NULL !=
-                   cmd->details.reserve_withdraw.reserve_reference);
-    ref = find_command (is,
-                        cmd->details.reserve_withdraw.reserve_reference);
-    GNUNET_assert (NULL != ref);
-    GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
-    if (NULL != cmd->details.reserve_withdraw.amount)
-    {
-      if (GNUNET_OK !=
-          TALER_string_to_amount (cmd->details.reserve_withdraw.amount,
-                                  &amount))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Failed to parse amount `%s' at %u\n",
-                    cmd->details.reserve_withdraw.amount,
-                    is->ip);
-        fail (is);
-        return;
-      }
-      cmd->details.reserve_withdraw.pk = find_pk (is->keys,
-                                                  &amount);
-    }
-    if (NULL == cmd->details.reserve_withdraw.pk)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Failed to determine denomination key at %u\n",
-                  is->ip);
-      fail (is);
-      return;
-    }
-
-    TALER_planchet_setup_random (&cmd->details.reserve_withdraw.ps);
-    cmd->details.reserve_withdraw.wsh
-      = TALER_EXCHANGE_reserve_withdraw (exchange,
-                                         cmd->details.reserve_withdraw.pk,
-                                         
&ref->details.admin_add_incoming.reserve_priv,
-                                         &cmd->details.reserve_withdraw.ps,
-                                         &reserve_withdraw_cb,
-                                         is);
-    if (NULL == cmd->details.reserve_withdraw.wsh)
-    {
-      GNUNET_break (0);
-      fail (is);
-      return;
-    }
-    return;
-  case OC_DEPOSIT:
-    {
-      struct GNUNET_HashCode h_contract_terms;
-      const struct TALER_CoinSpendPrivateKeyP *coin_priv;
-      const struct TALER_EXCHANGE_DenomPublicKey *coin_pk;
-      const struct TALER_DenominationSignature *coin_pk_sig;
-      struct TALER_CoinSpendPublicKeyP coin_pub;
-      struct TALER_CoinSpendSignatureP coin_sig;
-      struct GNUNET_TIME_Absolute refund_deadline;
-      struct GNUNET_TIME_Absolute wire_deadline;
-      struct GNUNET_TIME_Absolute timestamp;
-      struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
-      struct TALER_MerchantPublicKeyP merchant_pub;
-      json_t *contract_terms;
-      json_t *wire;
-
-      GNUNET_assert (NULL !=
-                     cmd->details.deposit.coin_ref);
-      ref = find_command (is,
-                          cmd->details.deposit.coin_ref);
-      GNUNET_assert (NULL != ref);
-      switch (ref->oc)
-      {
-      case OC_WITHDRAW_SIGN:
-        coin_priv = &ref->details.reserve_withdraw.ps.coin_priv;
-        coin_pk = ref->details.reserve_withdraw.pk;
-        coin_pk_sig = &ref->details.reserve_withdraw.sig;
-        break;
-      case OC_REFRESH_REVEAL:
-        {
-          const struct FreshCoin *fc;
-          unsigned int idx;
-
-          idx = cmd->details.deposit.coin_idx;
-          GNUNET_assert (idx < ref->details.refresh_reveal.num_fresh_coins);
-          fc = &ref->details.refresh_reveal.fresh_coins[idx];
-
-          coin_priv = &fc->coin_priv;
-          coin_pk = fc->pk;
-          coin_pk_sig = &fc->sig;
-        }
-        break;
-      default:
-        GNUNET_assert (0);
-      }
-      if (GNUNET_OK !=
-          TALER_string_to_amount (cmd->details.deposit.amount,
-                                  &amount))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Failed to parse amount `%s' at %u\n",
-                    cmd->details.deposit.amount,
-                    is->ip);
-        fail (is);
-        return;
-      }
-      contract_terms = json_loads (cmd->details.deposit.contract_terms,
-                             JSON_REJECT_DUPLICATES,
-                             NULL);
-      if (NULL == contract_terms)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Failed to parse proposal data `%s' at %u/%s\n",
-                    cmd->details.deposit.contract_terms,
-                    is->ip,
-                    cmd->label);
-        fail (is);
-        return;
-      }
-      GNUNET_assert (GNUNET_OK ==
-                     TALER_JSON_hash (contract_terms,
-                                      &h_contract_terms));
-      json_decref (contract_terms);
-      wire = json_loads (cmd->details.deposit.wire_details,
-                         JSON_REJECT_DUPLICATES,
-                         NULL);
-      if (NULL == wire)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Failed to parse wire details `%s' at %u/%s\n",
-                    cmd->details.deposit.wire_details,
-                    is->ip,
-                    cmd->label);
-        fail (is);
-        return;
-      }
-      GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
-                                          &coin_pub.eddsa_pub);
-
-      priv = GNUNET_CRYPTO_eddsa_key_create ();
-      cmd->details.deposit.merchant_priv.eddsa_priv = *priv;
-      GNUNET_free (priv);
-      if (0 != cmd->details.deposit.refund_deadline.rel_value_us)
-      {
-        refund_deadline = GNUNET_TIME_relative_to_absolute 
(cmd->details.deposit.refund_deadline);
-        wire_deadline = GNUNET_TIME_relative_to_absolute 
(GNUNET_TIME_relative_multiply (cmd->details.deposit.refund_deadline, 2));
-      }
-      else
-      {
-        refund_deadline = GNUNET_TIME_UNIT_ZERO_ABS;
-        wire_deadline = GNUNET_TIME_relative_to_absolute 
(GNUNET_TIME_UNIT_ZERO);
-      }
-      GNUNET_CRYPTO_eddsa_key_get_public 
(&cmd->details.deposit.merchant_priv.eddsa_priv,
-                                          &merchant_pub.eddsa_pub);
-
-      timestamp = GNUNET_TIME_absolute_get ();
-      GNUNET_TIME_round_abs (&timestamp);
-      GNUNET_TIME_round_abs (&refund_deadline);
-      GNUNET_TIME_round_abs (&wire_deadline);
-      {
-        struct TALER_DepositRequestPS dr;
-
-        memset (&dr,
-                0,
-                sizeof (dr));
-        dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
-        dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
-        dr.h_contract_terms = h_contract_terms;
-        GNUNET_assert (GNUNET_OK ==
-                       TALER_JSON_wire_signature_hash (wire,
-                                                       &dr.h_wire));
-        dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
-        dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
-        TALER_amount_hton (&dr.amount_with_fee,
-                           &amount);
-        TALER_amount_hton (&dr.deposit_fee,
-                           &coin_pk->fee_deposit);
-        dr.merchant = merchant_pub;
-        dr.coin_pub = coin_pub;
-        GNUNET_assert (GNUNET_OK ==
-                       GNUNET_CRYPTO_eddsa_sign (&coin_priv->eddsa_priv,
-                                                 &dr.purpose,
-                                                 &coin_sig.eddsa_signature));
-      }
-      cmd->details.deposit.dh
-        = TALER_EXCHANGE_deposit (exchange,
-                                  &amount,
-                                  wire_deadline,
-                                  wire,
-                                  &h_contract_terms,
-                                  &coin_pub,
-                                  coin_pk_sig,
-                                  &coin_pk->key,
-                                  timestamp,
-                                  &merchant_pub,
-                                  refund_deadline,
-                                  &coin_sig,
-                                  &deposit_cb,
-                                  is);
-      if (NULL == cmd->details.deposit.dh)
-      {
-        GNUNET_break (0);
-        json_decref (wire);
-        fail (is);
-        return;
-      }
-      json_decref (wire);
-      return;
-    }
-  case OC_REFRESH_MELT:
-    {
-      unsigned int num_fresh_coins;
-
-      cmd->details.refresh_melt.noreveal_index = UINT16_MAX;
-      for (num_fresh_coins=0;
-           NULL != cmd->details.refresh_melt.fresh_amounts[num_fresh_coins];
-           num_fresh_coins++) ;
-
-      cmd->details.refresh_melt.fresh_pks
-        = GNUNET_new_array (num_fresh_coins,
-                            const struct TALER_EXCHANGE_DenomPublicKey *);
-      {
-        struct TALER_CoinSpendPrivateKeyP melt_priv;
-        struct TALER_Amount melt_amount;
-        struct TALER_DenominationSignature melt_sig;
-        struct TALER_EXCHANGE_DenomPublicKey melt_pk;
-        struct TALER_EXCHANGE_DenomPublicKey fresh_pks[num_fresh_coins];
-        unsigned int i;
-
-        const struct MeltDetails *md = &cmd->details.refresh_melt.melted_coin;
-        ref = find_command (is,
-                            md->coin_ref);
-        GNUNET_assert (NULL != ref);
-        GNUNET_assert (OC_WITHDRAW_SIGN == ref->oc);
-
-        melt_priv = ref->details.reserve_withdraw.ps.coin_priv;
-        if (GNUNET_OK !=
-            TALER_string_to_amount (md->amount,
-                                    &melt_amount))
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                      "Failed to parse amount `%s' at %u\n",
-                      md->amount,
-                      is->ip);
-          fail (is);
-          return;
-        }
-        melt_sig = ref->details.reserve_withdraw.sig;
-        melt_pk = *ref->details.reserve_withdraw.pk;
-        for (i=0;i<num_fresh_coins;i++)
-        {
-          if (GNUNET_OK !=
-              TALER_string_to_amount 
(cmd->details.refresh_melt.fresh_amounts[i],
-                                      &amount))
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "Failed to parse amount `%s' at %u\n",
-                        cmd->details.reserve_withdraw.amount,
-                        is->ip);
-            fail (is);
-            return;
-          }
-          if (NULL ==
-              (cmd->details.refresh_melt.fresh_pks[i]
-               = find_pk (is->keys,
-                          &amount)))
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "Failed to find denomination key for amount `%s' at 
%u\n",
-                        cmd->details.reserve_withdraw.amount,
-                        is->ip);
-            fail (is);
-            return;
-          }
-          fresh_pks[i] = *cmd->details.refresh_melt.fresh_pks[i];
-        }
-        cmd->details.refresh_melt.refresh_data
-          = TALER_EXCHANGE_refresh_prepare (&melt_priv,
-                                            &melt_amount,
-                                            &melt_sig,
-                                            &melt_pk,
-                                            GNUNET_YES,
-                                            num_fresh_coins,
-                                            fresh_pks,
-                                            
&cmd->details.refresh_melt.refresh_data_length);
-        if (NULL == cmd->details.refresh_melt.refresh_data)
-        {
-          GNUNET_break (0);
-          fail (is);
-          return;
-        }
-        cmd->details.refresh_melt.rmh
-          = TALER_EXCHANGE_refresh_melt (exchange,
-                                     
cmd->details.refresh_melt.refresh_data_length,
-                                     cmd->details.refresh_melt.refresh_data,
-                                     &melt_cb,
-                                     is);
-        if (NULL == cmd->details.refresh_melt.rmh)
-        {
-          GNUNET_break (0);
-          fail (is);
-          return;
-        }
-      }
-    }
-    return;
-  case OC_REFRESH_REVEAL:
-    ref = find_command (is,
-                        cmd->details.refresh_reveal.melt_ref);
-    GNUNET_assert (NULL != ref);
-    cmd->details.refresh_reveal.rrh
-      = TALER_EXCHANGE_refresh_reveal (exchange,
-                                       
ref->details.refresh_melt.refresh_data_length,
-                                       ref->details.refresh_melt.refresh_data,
-                                       
ref->details.refresh_melt.noreveal_index,
-                                       &reveal_cb,
-                                       is);
-    if (NULL == cmd->details.refresh_reveal.rrh)
-    {
-      GNUNET_break (0);
-      fail (is);
-      return;
-    }
-    return;
-  case OC_REFRESH_LINK:
-    /* find reveal command */
-    ref = find_command (is,
-                        cmd->details.refresh_link.reveal_ref);
-    GNUNET_assert (NULL != ref);
-    /* find melt command */
-    ref = find_command (is,
-                        ref->details.refresh_reveal.melt_ref);
-    GNUNET_assert (NULL != ref);
-    /* find reserve_withdraw command */
-    {
-      const struct MeltDetails *md;
-
-      md = &ref->details.refresh_melt.melted_coin;
-      ref = find_command (is,
-                          md->coin_ref);
-      GNUNET_assert (NULL != ref);
-    }
-    GNUNET_assert (OC_WITHDRAW_SIGN == ref->oc);
-    /* finally, use private key from withdraw sign command */
-    cmd->details.refresh_link.rlh
-      = TALER_EXCHANGE_refresh_link (exchange,
-                                     
&ref->details.reserve_withdraw.ps.coin_priv,
-                                     &link_cb,
-                                     is);
-    if (NULL == cmd->details.refresh_link.rlh)
-    {
-      GNUNET_break (0);
-      fail (is);
-      return;
-    }
-    return;
-  case OC_WIRE:
-    cmd->details.wire.wh = TALER_EXCHANGE_wire (exchange,
-                                                &wire_cb,
-                                                is);
-    return;
-  case OC_WIRE_DEPOSITS:
-    if (NULL != cmd->details.wire_deposits.wtid_ref)
-    {
-      ref = find_command (is,
-                          cmd->details.wire_deposits.wtid_ref);
-      GNUNET_assert (NULL != ref);
-      switch (ref->oc)
-      {
-      case OC_DEPOSIT_WTID:
-        cmd->details.wire_deposits.wtid = ref->details.deposit_wtid.wtid;
-        break;
-      case OC_CHECK_BANK_TRANSFER:
-        GNUNET_assert (GNUNET_OK ==
-                       GNUNET_STRINGS_string_to_data 
(ref->details.check_bank_transfer.subject,
-                                                      strlen 
(ref->details.check_bank_transfer.subject),
-                                                      
&cmd->details.wire_deposits.wtid,
-                                                      sizeof 
(cmd->details.wire_deposits.wtid)));
-        break;
-      default:
-        GNUNET_break (0);
-        fail (is);
-        return;
-      }
-    }
-    cmd->details.wire_deposits.wdh
-      = TALER_EXCHANGE_track_transfer (exchange,
-                                      &cmd->details.wire_deposits.wtid,
-                                      &wire_deposits_cb,
-                                      is);
-    return;
-  case OC_DEPOSIT_WTID:
-    {
-      struct GNUNET_HashCode h_wire;
-      struct GNUNET_HashCode h_contract_terms;
-      json_t *wire;
-      json_t *contract_terms;
-      const struct Command *coin;
-      struct TALER_CoinSpendPublicKeyP coin_pub;
-
-      ref = find_command (is,
-                          cmd->details.deposit_wtid.deposit_ref);
-      GNUNET_assert (NULL != ref);
-      coin = find_command (is,
-                           ref->details.deposit.coin_ref);
-      GNUNET_assert (NULL != coin);
-      get_public_key_from_coin_command (coin,
-                                        ref->details.deposit.coin_idx,
-                                        &coin_pub);
-      wire = json_loads (ref->details.deposit.wire_details,
-                         JSON_REJECT_DUPLICATES,
-                         NULL);
-      GNUNET_assert (NULL != wire);
-      GNUNET_assert (GNUNET_OK ==
-                     TALER_JSON_wire_signature_hash (wire,
-                                                     &h_wire));
-      json_decref (wire);
-      contract_terms = json_loads (ref->details.deposit.contract_terms,
-                             JSON_REJECT_DUPLICATES,
-                             NULL);
-      GNUNET_assert (NULL != contract_terms);
-      GNUNET_assert (GNUNET_OK ==
-                     TALER_JSON_hash (contract_terms,
-                                      &h_contract_terms));
-      json_decref (contract_terms);
-      cmd->details.deposit_wtid.dwh
-          = TALER_EXCHANGE_track_transaction (exchange,
-                                              
&ref->details.deposit.merchant_priv,
-                                              &h_wire,
-                                              &h_contract_terms,
-                                              &coin_pub,
-                                              &deposit_wtid_cb,
-                                              is);
-    }
-    return;
-  case OC_RUN_AGGREGATOR:
-    {
-      const struct GNUNET_DISK_FileHandle *pr;
-
-      cmd->details.run_aggregator.aggregator_proc
-        = GNUNET_OS_start_process (GNUNET_NO,
-                                   GNUNET_OS_INHERIT_STD_ALL,
-                                   NULL, NULL, NULL,
-                                   "taler-exchange-aggregator",
-                                   "taler-exchange-aggregator",
-                                   "-c", "test_exchange_api.conf",
-                                   "-t", /* exit when done */
-                                   NULL);
-      if (NULL == cmd->details.run_aggregator.aggregator_proc)
-      {
-        GNUNET_break (0);
-        fail (is);
-        return;
-      }
-      pr = GNUNET_DISK_pipe_handle (sigpipe,
-                                    GNUNET_DISK_PIPE_END_READ);
-      cmd->details.run_aggregator.child_death_task
-        = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
-                                          pr,
-                                          &maint_child_death, is);
-      return;
-    }
-  case OC_RUN_WIREWATCH:
-    {
-      const struct GNUNET_DISK_FileHandle *pr;
-
-      cmd->details.run_wirewatch.wirewatch_proc
-        = GNUNET_OS_start_process (GNUNET_NO,
-                                   GNUNET_OS_INHERIT_STD_ALL,
-                                   NULL, NULL, NULL,
-                                   "taler-exchange-wirewatch",
-                                   "taler-exchange-wirewatch",
-                                   "-c", "test_exchange_api.conf",
-                                   "-T", /* exit when done */
-                                   NULL);
-      if (NULL == cmd->details.run_wirewatch.wirewatch_proc)
-      {
-        GNUNET_break (0);
-        fail (is);
-        return;
-      }
-      pr = GNUNET_DISK_pipe_handle (sigpipe,
-                                    GNUNET_DISK_PIPE_END_READ);
-      cmd->details.run_wirewatch.child_death_task
-        = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
-                                          pr,
-                                          &maint_child_death, is);
-      return;
-    }
-  case OC_CHECK_BANK_TRANSFER:
-    {
-      if (GNUNET_OK !=
-          TALER_string_to_amount (cmd->details.check_bank_transfer.amount,
-                                  &amount))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Failed to parse amount `%s' at %u\n",
-                    cmd->details.reserve_withdraw.amount,
-                    is->ip);
-        fail (is);
-        return;
-      }
-      if (GNUNET_OK !=
-          TALER_FAKEBANK_check (fakebank,
-                                &amount,
-                                cmd->details.check_bank_transfer.account_debit,
-                                
cmd->details.check_bank_transfer.account_credit,
-                                
cmd->details.check_bank_transfer.exchange_base_url,
-                                &cmd->details.check_bank_transfer.subject))
-      {
-        GNUNET_break (0);
-        fail (is);
-        return;
-      }
-      next_command (is);
-      return;
-    }
-  case OC_CHECK_BANK_TRANSFERS_EMPTY:
-    {
-      if (GNUNET_OK !=
-          TALER_FAKEBANK_check_empty (fakebank))
-      {
-        GNUNET_break (0);
-        fail (is);
-        return;
-      }
-      next_command (is);
-      return;
-    }
-  case OC_REFUND:
-    {
-      const struct Command *coin;
-      struct GNUNET_HashCode h_contract_terms;
-      json_t *contract_terms;
-      struct TALER_CoinSpendPublicKeyP coin_pub;
-      struct TALER_Amount refund_fee;
-
-      if (GNUNET_OK !=
-          TALER_string_to_amount (cmd->details.refund.amount,
-                                  &amount))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Failed to parse amount `%s' at %u\n",
-                    cmd->details.refund.amount,
-                    is->ip);
-        fail (is);
-        return;
-      }
-      if (GNUNET_OK !=
-          TALER_string_to_amount (cmd->details.refund.fee,
-                                  &refund_fee))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Failed to parse amount `%s' at %u\n",
-                    cmd->details.refund.fee,
-                    is->ip);
-        fail (is);
-        return;
-      }
-      ref = find_command (is,
-                          cmd->details.refund.deposit_ref);
-      GNUNET_assert (NULL != ref);
-      contract_terms = json_loads (ref->details.deposit.contract_terms,
-                             JSON_REJECT_DUPLICATES,
-                             NULL);
-      GNUNET_assert (NULL != contract_terms);
-      GNUNET_assert (GNUNET_OK ==
-                     TALER_JSON_hash (contract_terms,
-                                      &h_contract_terms));
-      json_decref (contract_terms);
-
-      coin = find_command (is,
-                           ref->details.deposit.coin_ref);
-      GNUNET_assert (NULL != coin);
-      get_public_key_from_coin_command (coin,
-                                        ref->details.deposit.coin_idx,
-                                        &coin_pub);
-      cmd->details.refund.rh
-        = TALER_EXCHANGE_refund (exchange,
-                                 &amount,
-                                 &refund_fee,
-                                 &h_contract_terms,
-                                 &coin_pub,
-                                 cmd->details.refund.rtransaction_id,
-                                 &ref->details.deposit.merchant_priv,
-                                 &refund_cb,
-                                 is);
-      if (NULL == cmd->details.refund.rh)
-      {
-        GNUNET_break (0);
-        fail (is);
-        return;
-      }
-      return;
-    }
-  case OC_REVOKE:
-    {
-      const struct GNUNET_DISK_FileHandle *pr;
-      char *dhks;
-      const struct Command *ref;
-
-      ref = find_command (is,
-                          cmd->details.revoke.ref);
-      GNUNET_assert (NULL != ref);
-      dhks = GNUNET_STRINGS_data_to_string_alloc 
(&ref->details.reserve_withdraw.pk->h_key,
-                                                  sizeof (struct 
GNUNET_HashCode));
-      cmd->details.revoke.revoke_proc
-        = GNUNET_OS_start_process (GNUNET_NO,
-                                   GNUNET_OS_INHERIT_STD_ALL,
-                                   NULL, NULL, NULL,
-                                   "taler-exchange-keyup",
-                                   "taler-exchange-keyup",
-                                   "-c", "test_exchange_api.conf",
-                                   "-r", dhks,
-                                   NULL);
-      GNUNET_free (dhks);
-      if (NULL == cmd->details.revoke.revoke_proc)
-      {
-        GNUNET_break (0);
-        fail (is);
-        return;
-      }
-      pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
-      cmd->details.revoke.child_death_task
-        = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
-                                          pr,
-                                          &maint_child_death,
-                                          is);
-      return;
-    }
-  case OC_PAYBACK:
-    {
-      const struct Command *ref;
-
-      ref = find_command (is,
-                          cmd->details.payback.ref);
-      GNUNET_assert (NULL != ref);
-      cmd->details.payback.ph
-        = TALER_EXCHANGE_payback (exchange,
-                                  ref->details.reserve_withdraw.pk,
-                                  &ref->details.reserve_withdraw.sig,
-                                  &ref->details.reserve_withdraw.ps,
-                                  &payback_cb,
-                                  is);
-      return;
-    }
-
-  default:
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unknown instruction %d at %u (%s)\n",
-                cmd->oc,
-                is->ip,
-                cmd->label);
-    fail (is);
-    return;
-  }
-}
-
-
-/**
- * Signal handler called for SIGCHLD.  Triggers the
- * respective handler by writing to the trigger pipe.
- */
-static void
-sighandler_child_death ()
-{
-  static char c;
-  int old_errno = errno;       /* back-up errno */
-
-  GNUNET_break (1 ==
-               GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
-                                       (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
-                                       &c, sizeof (c)));
-  errno = old_errno;           /* restore errno */
-}
-
-
-/**
- * Function run when the test terminates (good or bad) with timeout.
- *
- * @param cls NULL
- */
-static void
-do_timeout (void *cls)
-{
-  timeout_task = NULL;
-  GNUNET_SCHEDULER_shutdown ();
-}
-
-
-/**
- * Function run when the test terminates (good or bad).
- * Cleans up our state.
- *
- * @param cls the interpreter state.
- */
-static void
-do_shutdown (void *cls)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd;
-
-  fprintf (stderr,
-           "Executing shutdown at `%s'\n",
-           is->commands[is->ip].label);
-
-  for (unsigned int i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
-  {
-    switch (cmd->oc)
-    {
-    case OC_END:
-      GNUNET_assert (0);
-      break;
-    case OC_ADMIN_ADD_INCOMING:
-      if (NULL != cmd->details.admin_add_incoming.aih)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Command %u (%s) did not complete\n",
-                    i,
-                    cmd->label);
-        TALER_BANK_admin_add_incoming_cancel 
(cmd->details.admin_add_incoming.aih);
-        cmd->details.admin_add_incoming.aih = NULL;
-      }
-      break;
-    case OC_WITHDRAW_STATUS:
-      if (NULL != cmd->details.reserve_status.wsh)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Command %u (%s) did not complete\n",
-                    i,
-                    cmd->label);
-        TALER_EXCHANGE_reserve_status_cancel (cmd->details.reserve_status.wsh);
-        cmd->details.reserve_status.wsh = NULL;
-      }
-      break;
-    case OC_WITHDRAW_SIGN:
-      if (NULL != cmd->details.reserve_withdraw.wsh)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Command %u (%s) did not complete\n",
-                    i,
-                    cmd->label);
-        TALER_EXCHANGE_reserve_withdraw_cancel 
(cmd->details.reserve_withdraw.wsh);
-        cmd->details.reserve_withdraw.wsh = NULL;
-      }
-      if (NULL != cmd->details.reserve_withdraw.sig.rsa_signature)
-      {
-        GNUNET_CRYPTO_rsa_signature_free 
(cmd->details.reserve_withdraw.sig.rsa_signature);
-        cmd->details.reserve_withdraw.sig.rsa_signature = NULL;
-      }
-      break;
-    case OC_DEPOSIT:
-      if (NULL != cmd->details.deposit.dh)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Command %u (%s) did not complete\n",
-                    i,
-                    cmd->label);
-        TALER_EXCHANGE_deposit_cancel (cmd->details.deposit.dh);
-        cmd->details.deposit.dh = NULL;
-      }
-      break;
-    case OC_REFRESH_MELT:
-      if (NULL != cmd->details.refresh_melt.rmh)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Command %u (%s) did not complete\n",
-                    i,
-                    cmd->label);
-        TALER_EXCHANGE_refresh_melt_cancel (cmd->details.refresh_melt.rmh);
-        cmd->details.refresh_melt.rmh = NULL;
-      }
-      GNUNET_free_non_null (cmd->details.refresh_melt.fresh_pks);
-      cmd->details.refresh_melt.fresh_pks = NULL;
-      GNUNET_free_non_null (cmd->details.refresh_melt.refresh_data);
-      cmd->details.refresh_melt.refresh_data = NULL;
-      cmd->details.refresh_melt.refresh_data_length = 0;
-      break;
-    case OC_REFRESH_REVEAL:
-      if (NULL != cmd->details.refresh_reveal.rrh)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Command %u (%s) did not complete\n",
-                    i,
-                    cmd->label);
-        TALER_EXCHANGE_refresh_reveal_cancel (cmd->details.refresh_reveal.rrh);
-        cmd->details.refresh_reveal.rrh = NULL;
-      }
-      {
-        unsigned int j;
-        struct FreshCoin *fresh_coins;
-
-        fresh_coins = cmd->details.refresh_reveal.fresh_coins;
-        for (j=0;j<cmd->details.refresh_reveal.num_fresh_coins;j++)
-          GNUNET_CRYPTO_rsa_signature_free (fresh_coins[j].sig.rsa_signature);
-      }
-      GNUNET_free_non_null (cmd->details.refresh_reveal.fresh_coins);
-      cmd->details.refresh_reveal.fresh_coins = NULL;
-      cmd->details.refresh_reveal.num_fresh_coins = 0;
-      break;
-    case OC_REFRESH_LINK:
-      if (NULL != cmd->details.refresh_link.rlh)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Command %u (%s) did not complete\n",
-                    i,
-                    cmd->label);
-        TALER_EXCHANGE_refresh_link_cancel (cmd->details.refresh_link.rlh);
-        cmd->details.refresh_link.rlh = NULL;
-      }
-      break;
-    case OC_WIRE:
-      if (NULL != cmd->details.wire.wh)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Command %u (%s) did not complete\n",
-                    i,
-                    cmd->label);
-        TALER_EXCHANGE_wire_cancel (cmd->details.wire.wh);
-        cmd->details.wire.wh = NULL;
-      }
-      break;
-    case OC_WIRE_DEPOSITS:
-      if (NULL != cmd->details.wire_deposits.wdh)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Command %u (%s) did not complete\n",
-                    i,
-                    cmd->label);
-        TALER_EXCHANGE_track_transfer_cancel (cmd->details.wire_deposits.wdh);
-        cmd->details.wire_deposits.wdh = NULL;
-      }
-      break;
-    case OC_DEPOSIT_WTID:
-      if (NULL != cmd->details.deposit_wtid.dwh)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Command %u (%s) did not complete\n",
-                    i,
-                    cmd->label);
-        TALER_EXCHANGE_track_transaction_cancel 
(cmd->details.deposit_wtid.dwh);
-        cmd->details.deposit_wtid.dwh = NULL;
-      }
-      break;
-    case OC_RUN_AGGREGATOR:
-      if (NULL != cmd->details.run_aggregator.aggregator_proc)
-      {
-        GNUNET_break (0 ==
-                      GNUNET_OS_process_kill 
(cmd->details.run_aggregator.aggregator_proc,
-                                              SIGKILL));
-        GNUNET_OS_process_wait (cmd->details.run_aggregator.aggregator_proc);
-        GNUNET_OS_process_destroy 
(cmd->details.run_aggregator.aggregator_proc);
-        cmd->details.run_aggregator.aggregator_proc = NULL;
-      }
-      if (NULL != cmd->details.run_aggregator.child_death_task)
-      {
-        GNUNET_SCHEDULER_cancel (cmd->details.run_aggregator.child_death_task);
-        cmd->details.run_aggregator.child_death_task = NULL;
-      }
-      break;
-    case OC_RUN_WIREWATCH:
-      if (NULL != cmd->details.run_wirewatch.wirewatch_proc)
-      {
-        GNUNET_break (0 ==
-                      GNUNET_OS_process_kill 
(cmd->details.run_wirewatch.wirewatch_proc,
-                                              SIGKILL));
-        GNUNET_OS_process_wait (cmd->details.run_wirewatch.wirewatch_proc);
-        GNUNET_OS_process_destroy (cmd->details.run_wirewatch.wirewatch_proc);
-        cmd->details.run_wirewatch.wirewatch_proc = NULL;
-      }
-      if (NULL != cmd->details.run_wirewatch.child_death_task)
-      {
-        GNUNET_SCHEDULER_cancel (cmd->details.run_wirewatch.child_death_task);
-        cmd->details.run_wirewatch.child_death_task = NULL;
-      }
-      break;
-    case OC_CHECK_BANK_TRANSFER:
-      GNUNET_free_non_null (cmd->details.check_bank_transfer.subject);
-      cmd->details.check_bank_transfer.subject = NULL;
-      break;
-    case OC_CHECK_BANK_TRANSFERS_EMPTY:
-      break;
-    case OC_REFUND:
-      if (NULL != cmd->details.refund.rh)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Command %u (%s) did not complete\n",
-                    i,
-                    cmd->label);
-        TALER_EXCHANGE_refund_cancel (cmd->details.refund.rh);
-        cmd->details.refund.rh = NULL;
-      }
-      break;
-    case OC_REVOKE:
-      if (NULL != cmd->details.revoke.revoke_proc)
-      {
-        GNUNET_break (0 ==
-                      GNUNET_OS_process_kill (cmd->details.revoke.revoke_proc,
-                                              SIGKILL));
-        GNUNET_OS_process_wait (cmd->details.revoke.revoke_proc);
-        GNUNET_OS_process_destroy (cmd->details.revoke.revoke_proc);
-        cmd->details.revoke.revoke_proc = NULL;
-      }
-      if (NULL != cmd->details.revoke.child_death_task)
-      {
-        GNUNET_SCHEDULER_cancel (cmd->details.revoke.child_death_task);
-        cmd->details.revoke.child_death_task = NULL;
-      }
-      break;
-    case OC_PAYBACK:
-      if (NULL != cmd->details.payback.ph)
-      {
-        TALER_EXCHANGE_payback_cancel (cmd->details.payback.ph);
-        cmd->details.payback.ph = NULL;
-      }
-      break;
-    default:
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Unknown instruction %d at %u (%s)\n",
-                  cmd->oc,
-                  i,
-                  cmd->label);
-      break;
-    }
-  }
-  if (NULL != is->task)
-  {
-    GNUNET_SCHEDULER_cancel (is->task);
-    is->task = NULL;
-  }
-  GNUNET_free (is);
-  if (NULL != fakebank)
-  {
-    TALER_FAKEBANK_stop (fakebank);
-    fakebank = NULL;
-  }
-  if (NULL != exchange)
-  {
-    TALER_EXCHANGE_disconnect (exchange);
-    exchange = NULL;
-  }
-  if (NULL != ctx)
-  {
-    GNUNET_CURL_fini (ctx);
-    ctx = NULL;
-  }
-  if (NULL != rc)
-  {
-    GNUNET_CURL_gnunet_rc_destroy (rc);
-    rc = NULL;
-  }
-  if (NULL != timeout_task)
-  {
-    GNUNET_SCHEDULER_cancel (timeout_task);
-    timeout_task = NULL;
-  }
-}
-
-
-/**
- * Functions of this type are called to provide the retrieved signing and
- * denomination keys of the exchange.  No TALER_EXCHANGE_*() functions should 
be called
- * in this callback.
- *
- * @param cls closure
- * @param keys information about keys of the exchange
- * @param vc version compatibility
- */
-static void
-cert_cb (void *cls,
-         const struct TALER_EXCHANGE_Keys *keys,
-        enum TALER_EXCHANGE_VersionCompatibility vc)
-{
-  struct InterpreterState *is = cls;
-
-  /* check that keys is OK */
-#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); 
GNUNET_SCHEDULER_shutdown(); return; } while (0)
-  ERR (NULL == keys);
-  ERR (0 == keys->num_sign_keys);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Read %u signing keys\n",
-              keys->num_sign_keys);
-  ERR (0 == keys->num_denom_keys);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Read %u denomination keys\n",
-              keys->num_denom_keys);
-#undef ERR
-
-  /* run actual tests via interpreter-loop */
-  is->keys = keys;
-  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
-                                       is);
-}
-
-
-/**
- * Main function that will be run by the scheduler.
- *
- * @param cls closure
- */
-static void
-run (void *cls)
-{
-  struct InterpreterState *is;
-  static const char *melt_fresh_amounts_1[] = {
-    "EUR:1",
-    "EUR:1",
-    "EUR:1",
-    "EUR:0.1",
-    "EUR:0.1",
-    "EUR:0.1",
-    "EUR:0.1",
-    "EUR:0.1",
-    "EUR:0.1",
-    "EUR:0.1",
-    "EUR:0.1",
-    "EUR:0.01",
-    "EUR:0.01",
-    "EUR:0.01",
-    "EUR:0.01",
-    "EUR:0.01",
-    "EUR:0.01",
-    /* with 0.01 withdraw fees (except for 1ct coins),
-       this totals up to exactly EUR:3.97, and with
-       the 0.03 refresh fee, to EUR:4.0*/
-    NULL
-  };
-  static struct Command commands[] =
-  {
-    /* *************** start of /wire testing ************** */
-
-#if WIRE_TEST
-    { .oc = OC_WIRE,
-      .label = "wire-test",
-      /* expecting 'test' method in response */
-      .expected_response_code = MHD_HTTP_OK,
-      .details.wire.format = "x-taler-bank",
-      .details.wire.expected_fee = "EUR:0.01" },
-#endif
-#if WIRE_EBICS
-    { .oc = OC_WIRE,
-      .label = "wire-sepa",
-      /* expecting 'ebics' method in response */
-      .expected_response_code = MHD_HTTP_OK,
-      .details.wire.format = "ebics",
-      .details.wire.expected_fee = "EUR:0.01" },
-#endif
-    /* *************** end of /wire testing ************** */
-
-#if WIRE_TEST
-    /* None of this works if 'test' is not allowed as we do
-       /admin/add/incoming with format 'test' */
-
-    /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */
-    { .oc = OC_ADMIN_ADD_INCOMING,
-      .label = "create-reserve-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.debit_account_no = 42,
-      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
-      .details.admin_add_incoming.auth_username = "user42",
-      .details.admin_add_incoming.auth_password = "pass42",
-      .details.admin_add_incoming.amount = "EUR:5.01" },
-    /* Run wirewatch to observe /admin/add/incoming */
-    { .oc = OC_RUN_WIREWATCH,
-      .label = "wirewatch-1" },
-    /* Withdraw a 5 EUR coin, at fee of 1 ct */
-    { .oc = OC_WITHDRAW_SIGN,
-      .label = "withdraw-coin-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.reserve_withdraw.reserve_reference = "create-reserve-1",
-      .details.reserve_withdraw.amount = "EUR:5" },
-    /* Check that deposit and withdraw operation are in history, and
-       that the balance is now at zero */
-    { .oc = OC_WITHDRAW_STATUS,
-      .label = "withdraw-status-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.reserve_status.reserve_reference = "create-reserve-1",
-      .details.reserve_status.expected_balance = "EUR:0" },
-    /* Try to deposit the 5 EUR coin (in full) */
-    { .oc = OC_DEPOSIT,
-      .label = "deposit-simple",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.deposit.amount = "EUR:5",
-      .details.deposit.coin_ref = "withdraw-coin-1",
-      .details.deposit.wire_details = "{ 
\"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\"  }",
-      .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice 
cream\", \"value\":1 } ] }" },
-
-    /* Try to overdraw funds ... */
-    { .oc = OC_WITHDRAW_SIGN,
-      .label = "withdraw-coin-2",
-      .expected_response_code = MHD_HTTP_FORBIDDEN,
-      .details.reserve_withdraw.reserve_reference = "create-reserve-1",
-      .details.reserve_withdraw.amount = "EUR:5" },
-
-    /* Try to double-spend the 5 EUR coin with different wire details */
-    { .oc = OC_DEPOSIT,
-      .label = "deposit-double-1",
-      .expected_response_code = MHD_HTTP_FORBIDDEN,
-      .details.deposit.amount = "EUR:5",
-      .details.deposit.coin_ref = "withdraw-coin-1",
-      .details.deposit.wire_details = "{ 
\"url\":\"payto://x-taler-bank/localhost:8082/43\", \"salt\":\"my salt\"  }",
-      .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice 
cream\", \"value\":1 } ] }" },
-    /* Try to double-spend the 5 EUR coin at the same merchant (but different
-       transaction ID) */
-    { .oc = OC_DEPOSIT,
-      .label = "deposit-double-2",
-      .expected_response_code = MHD_HTTP_FORBIDDEN,
-      .details.deposit.amount = "EUR:5",
-      .details.deposit.coin_ref = "withdraw-coin-1",
-      .details.deposit.wire_details = "{ 
\"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\"  }",
-      .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice 
cream\", \"value\":1 } ] }" },
-    /* Try to double-spend the 5 EUR coin at the same merchant (but different
-       proposal) */
-    { .oc = OC_DEPOSIT,
-      .label = "deposit-double-3",
-      .expected_response_code = MHD_HTTP_FORBIDDEN,
-      .details.deposit.amount = "EUR:5",
-      .details.deposit.coin_ref = "withdraw-coin-1",
-      .details.deposit.wire_details = "{ 
\"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\"  }",
-      .details.deposit.contract_terms = "{ \"items\":[{ \"name\":\"ice 
cream\", \"value\":2 } ] }" },
-
-    /* ***************** /refresh testing ******************** */
-
-    /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct */
-    { .oc = OC_ADMIN_ADD_INCOMING,
-      .label = "refresh-create-reserve-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.debit_account_no = 424,
-      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
-      .details.admin_add_incoming.auth_username = "user424",
-      .details.admin_add_incoming.auth_password = "pass424",
-      .details.admin_add_incoming.amount = "EUR:5.01" },
-    /* Run wirewatch to observe /admin/add/incoming */
-    { .oc = OC_RUN_WIREWATCH,
-      .label = "wirewatch-2" },
-    /* Withdraw a 5 EUR coin, at fee of 1 ct */
-    { .oc = OC_WITHDRAW_SIGN,
-      .label = "refresh-withdraw-coin-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.reserve_withdraw.reserve_reference = "refresh-create-reserve-1",
-      .details.reserve_withdraw.amount = "EUR:5" },
-    /* Try to partially spend (deposit) 1 EUR of the 5 EUR coin (in full)
-       (merchant would receive EUR:0.99 due to 1 ct deposit fee) */
-    { .oc = OC_DEPOSIT,
-      .label = "refresh-deposit-partial",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.deposit.amount = "EUR:1",
-      .details.deposit.coin_ref = "refresh-withdraw-coin-1",
-      .details.deposit.wire_details = "{ 
\"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\"  }",
-      .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice 
cream\", \"value\":\"EUR:1\" } ] }" },
-
-    /* Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x 
EUR:0.13) */
-
-    { .oc = OC_REFRESH_MELT,
-      .label = "refresh-melt-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.refresh_melt.melted_coin = {
-        .amount = "EUR:4",
-        .coin_ref = "refresh-withdraw-coin-1" },
-      .details.refresh_melt.fresh_amounts = melt_fresh_amounts_1 },
-
-
-    /* Complete (successful) melt operation, and withdraw the coins */
-    { .oc = OC_REFRESH_REVEAL,
-      .label = "refresh-reveal-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.refresh_reveal.melt_ref = "refresh-melt-1" },
-
-    /* do it again to check idempotency */
-    { .oc = OC_REFRESH_REVEAL,
-      .label = "refresh-reveal-1-idempotency",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.refresh_reveal.melt_ref = "refresh-melt-1" },
-
-    /* Test that /refresh/link works */
-    { .oc = OC_REFRESH_LINK,
-      .label = "refresh-link-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.refresh_link.reveal_ref = "refresh-reveal-1" },
-
-
-    /* Test successfully spending coins from the refresh operation:
-       first EUR:1 */
-    { .oc = OC_DEPOSIT,
-      .label = "refresh-deposit-refreshed-1a",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.deposit.amount = "EUR:1",
-      .details.deposit.coin_ref = "refresh-reveal-1-idempotency",
-      .details.deposit.coin_idx = 0,
-      .details.deposit.wire_details = "{ 
\"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\"  }",
-      .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice 
cream\", \"value\":3 } ] }" },
-
-    /* Test successfully spending coins from the refresh operation:
-       finally EUR:0.1 */
-    { .oc = OC_DEPOSIT,
-      .label = "refresh-deposit-refreshed-1b",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.deposit.amount = "EUR:0.1",
-      .details.deposit.coin_ref = "refresh-reveal-1",
-      .details.deposit.coin_idx = 4,
-      .details.deposit.wire_details = "{ 
\"url\":\"payto://x-taler-bank/localhost:8082/43\", \"salt\":\"my salt\"  }",
-      .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice 
cream\", \"value\":3 } ] }" },
-
-    /* Test running a failing melt operation (same operation again must fail) 
*/
-    { .oc = OC_REFRESH_MELT,
-      .label = "refresh-melt-failing",
-      .expected_response_code = MHD_HTTP_FORBIDDEN,
-      .details.refresh_melt.melted_coin = {
-        .amount = "EUR:4",
-        .coin_ref = "refresh-withdraw-coin-1" },
-      .details.refresh_melt.fresh_amounts = melt_fresh_amounts_1 },
-
-    // FIXME: also test with coin that was already melted
-    // (signature differs from coin that was deposited...)
-    /* *************** end of /refresh testing ************** */
-
-    /* ************** Test tracking API ******************** */
-    /* Try resolving a deposit's WTID, as we never triggered
-       execution of transactions, the answer should be that
-       the exchange knows about the deposit, but has no WTID yet. */
-    { .oc = OC_DEPOSIT_WTID,
-      .label = "deposit-wtid-found",
-      .expected_response_code = MHD_HTTP_ACCEPTED,
-      .details.deposit_wtid.deposit_ref = "deposit-simple" },
-    /* Try resolving a deposit's WTID for a failed deposit.
-       As the deposit failed, the answer should be that
-       the exchange does NOT know about the deposit. */
-    { .oc = OC_DEPOSIT_WTID,
-      .label = "deposit-wtid-failing",
-      .expected_response_code = MHD_HTTP_NOT_FOUND,
-      .details.deposit_wtid.deposit_ref = "deposit-double-2" },
-    /* Try resolving an undefined (all zeros) WTID; this
-       should fail as obviously the exchange didn't use that
-       WTID value for any transaction. */
-    { .oc = OC_WIRE_DEPOSITS,
-      .label = "wire-deposit-failing",
-      .expected_response_code = MHD_HTTP_NOT_FOUND },
-
-    /* Run transfers. Note that _actual_ aggregation will NOT
-       happen here, as each deposit operation is run with a
-       fresh merchant public key! */
-    { .oc = OC_RUN_AGGREGATOR,
-      .label = "run-aggregator" },
-
-    { .oc = OC_CHECK_BANK_TRANSFER,
-      .label = "check_bank_transfer-499c",
-      .details.check_bank_transfer.exchange_base_url = 
"http://localhost:8081/";,
-      .details.check_bank_transfer.amount = "EUR:4.98",
-      .details.check_bank_transfer.account_debit = 2,
-      .details.check_bank_transfer.account_credit = 42
-    },
-    { .oc = OC_CHECK_BANK_TRANSFER,
-      .label = "check_bank_transfer-99c1",
-      .details.check_bank_transfer.exchange_base_url = 
"http://localhost:8081/";,
-      .details.check_bank_transfer.amount = "EUR:0.98",
-      .details.check_bank_transfer.account_debit = 2,
-      .details.check_bank_transfer.account_credit = 42
-    },
-    { .oc = OC_CHECK_BANK_TRANSFER,
-      .label = "check_bank_transfer-99c2",
-      .details.check_bank_transfer.exchange_base_url = 
"http://localhost:8081/";,
-      .details.check_bank_transfer.amount = "EUR:0.98",
-      .details.check_bank_transfer.account_debit = 2,
-      .details.check_bank_transfer.account_credit = 42
-    },
-    { .oc = OC_CHECK_BANK_TRANSFER,
-      .label = "check_bank_transfer-9c",
-      .details.check_bank_transfer.exchange_base_url = 
"http://localhost:8081/";,
-      .details.check_bank_transfer.amount = "EUR:0.08",
-      .details.check_bank_transfer.account_debit = 2,
-      .details.check_bank_transfer.account_credit = 43
-    },
-    { .oc = OC_CHECK_BANK_TRANSFER,
-      .label = "check_bank_transfer-aai-1",
-      .details.check_bank_transfer.exchange_base_url = 
"http://localhost:8081/";,
-      .details.check_bank_transfer.amount = "EUR:5.01",
-      .details.check_bank_transfer.account_debit = 42,
-      .details.check_bank_transfer.account_credit = 2
-    },
-    { .oc = OC_CHECK_BANK_TRANSFER,
-      .label = "check_bank_transfer-aai-2",
-      .details.check_bank_transfer.exchange_base_url = 
"http://localhost:8081/";,
-      .details.check_bank_transfer.amount = "EUR:5.01",
-      .details.check_bank_transfer.account_debit = 424,
-      .details.check_bank_transfer.account_credit = 2
-    },
-
-    { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
-      .label = "check_bank_empty" },
-    { .oc = OC_DEPOSIT_WTID,
-      .label = "deposit-wtid-ok",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.deposit_wtid.deposit_ref = "deposit-simple",
-      .details.deposit_wtid.bank_transfer_ref = "check_bank_transfer-499c" },
-
-    { .oc = OC_WIRE_DEPOSITS,
-      .label = "wire-deposits-success-bank",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.wire_deposits.wtid_ref = "check_bank_transfer-99c1",
-      .details.wire_deposits.total_amount_expected = "EUR:0.98",
-      .details.wire_deposits.wire_fee_expected = "EUR:0.01"  },
-
-    { .oc = OC_WIRE_DEPOSITS,
-      .label = "wire-deposits-success-wtid",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.wire_deposits.wtid_ref = "deposit-wtid-ok",
-      .details.wire_deposits.total_amount_expected = "EUR:4.98",
-      .details.wire_deposits.wire_fee_expected = "EUR:0.01"  },
-
-    /* ************** End of tracking API testing************* */
-
-    /* ************** Test /refund API  ************* */
-
-
-    /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */
-    { .oc = OC_ADMIN_ADD_INCOMING,
-      .label = "create-reserve-r1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.debit_account_no = 42,
-      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
-      .details.admin_add_incoming.auth_username = "user42",
-      .details.admin_add_incoming.auth_password = "pass42",
-      .details.admin_add_incoming.amount = "EUR:5.01" },
-    /* Run wirewatch to observe /admin/add/incoming */
-    { .oc = OC_RUN_WIREWATCH,
-      .label = "wirewatch-3" },
-    /* Withdraw a 5 EUR coin, at fee of 1 ct */
-    { .oc = OC_WITHDRAW_SIGN,
-      .label = "withdraw-coin-r1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.reserve_withdraw.reserve_reference = "create-reserve-r1",
-      .details.reserve_withdraw.amount = "EUR:5" },
-    /* Spend 5 EUR of the 5 EUR coin (in full)
-       (merchant would receive EUR:4.99 due to 1 ct deposit fee) */
-    { .oc = OC_DEPOSIT,
-      .label = "deposit-refund-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.deposit.amount = "EUR:5",
-      .details.deposit.coin_ref = "withdraw-coin-r1",
-      .details.deposit.wire_details = "{ 
\"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\"  }",
-      .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice 
cream\", \"value\":\"EUR:5\" } ] }",
-      .details.deposit.refund_deadline = { 60LL * 1000 * 1000 } /* 60 s */,
-    },
-    /* Run transfers. Should do nothing as refund deadline blocks it */
-    { .oc = OC_RUN_AGGREGATOR,
-      .label = "run-aggregator-refund" },
-    { .oc = OC_CHECK_BANK_TRANSFER,
-      .label = "check_bank_transfer-aai-3",
-      .details.check_bank_transfer.exchange_base_url = 
"http://localhost:8081/";,
-      .details.check_bank_transfer.amount = "EUR:5.01",
-      .details.check_bank_transfer.account_debit = 42,
-      .details.check_bank_transfer.account_credit = 2
-    },
-    /* check that aggregator didn't do anything, as expected */
-    { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
-      .label = "check-refund-not-run" },
-    /* Trigger refund */
-    { .oc = OC_REFUND,
-      .label = "refund-ok",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.refund.amount = "EUR:5",
-      .details.refund.fee = "EUR:0.01",
-      .details.refund.deposit_ref = "deposit-refund-1",
-    },
-    /* Spend 4.99 EUR of the refunded 4.99 EUR coin (1ct gone due to refund)
-       (merchant would receive EUR:4.98 due to 1 ct deposit fee) */
-    { .oc = OC_DEPOSIT,
-      .label = "deposit-refund-2",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.deposit.amount = "EUR:4.99",
-      .details.deposit.coin_ref = "withdraw-coin-r1",
-      .details.deposit.wire_details = "{ 
\"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\"  }",
-      .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"more ice 
cream\", \"value\":\"EUR:5\" } ] }",
-    },
-    /* Run transfers. This will do the transfer as refund deadline was 0 */
-    { .oc = OC_RUN_AGGREGATOR,
-      .label = "run-aggregator-3" },
-    /* Check that deposit did run */
-    { .oc = OC_CHECK_BANK_TRANSFER,
-      .label = "check_bank_transfer-pre-refund",
-      .details.check_bank_transfer.exchange_base_url = 
"http://localhost:8081/";,
-      .details.check_bank_transfer.amount = "EUR:4.97",
-      .details.check_bank_transfer.account_debit = 2,
-      .details.check_bank_transfer.account_credit = 42
-    },
-    /* Run failing refund, as past deadline & aggregation */
-    { .oc = OC_REFUND,
-      .label = "refund-fail",
-      .expected_response_code = MHD_HTTP_GONE,
-      .details.refund.amount = "EUR:4.99",
-      .details.refund.fee = "EUR:0.01",
-      .details.refund.deposit_ref = "deposit-refund-2",
-    },
-    { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
-      .label = "check-empty-after-refund" },
-
-
-    /* Test refunded coins are never executed, even past
-       refund deadline */
-    { .oc = OC_ADMIN_ADD_INCOMING,
-      .label = "create-reserve-rb",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.debit_account_no = 42,
-      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
-      .details.admin_add_incoming.auth_username = "user42",
-      .details.admin_add_incoming.auth_password = "pass42",
-      .details.admin_add_incoming.amount = "EUR:5.01" },
-    /* Run wirewatch to observe /admin/add/incoming */
-    { .oc = OC_RUN_WIREWATCH,
-      .label = "wirewatch-3b" },
-    /* Withdraw a 5 EUR coin, at fee of 1 ct */
-    { .oc = OC_WITHDRAW_SIGN,
-      .label = "withdraw-coin-rb",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.reserve_withdraw.reserve_reference = "create-reserve-rb",
-      .details.reserve_withdraw.amount = "EUR:5" },
-    /* Spend 5 EUR of the 5 EUR coin (in full)
-       (merchant would receive EUR:4.99 due to 1 ct deposit fee) */
-    { .oc = OC_DEPOSIT,
-      .label = "deposit-refund-1b",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.deposit.amount = "EUR:5",
-      .details.deposit.coin_ref = "withdraw-coin-rb",
-      .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice 
cream\", \"value\":\"EUR:5\" } ] }",
-      .details.deposit.refund_deadline = { 0 },
-      .details.deposit.wire_details = "{ 
\"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\"  }"
-    },
-    { .oc = OC_CHECK_BANK_TRANSFER,
-      .label = "check_bank_transfer-aai-3b",
-      .details.check_bank_transfer.exchange_base_url = 
"http://localhost:8081/";,
-      .details.check_bank_transfer.amount = "EUR:5.01",
-      .details.check_bank_transfer.account_debit = 42,
-      .details.check_bank_transfer.account_credit = 2
-    },
-    /* Trigger refund (before aggregator had a chance to execute
-       deposit, even though refund deadline was zero) */
-    { .oc = OC_REFUND,
-      .label = "refund-ok-fast",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.refund.amount = "EUR:5",
-      .details.refund.fee = "EUR:0.01",
-      .details.refund.deposit_ref = "deposit-refund-1b",
-    },
-    /* Run transfers. This will do the transfer as refund deadline
-       was 0, except of course because the refund succeeded, the
-       transfer should no longer be done. */
-    { .oc = OC_RUN_AGGREGATOR,
-      .label = "run-aggregator-3b" },
-    /* check that aggregator didn't do anything, as expected */
-    { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
-      .label = "check-refund-fast-not-run" },
-
-
-    /* ************** End of refund API testing************* */
-
-    /* ************** Test /payback API  ************* */
-
-    /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config,
-       then withdraw a coin and then have it be paid back. */
-    { .oc = OC_ADMIN_ADD_INCOMING,
-      .label = "payback-create-reserve-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.debit_account_no = 42,
-      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
-      .details.admin_add_incoming.auth_username = "user42",
-      .details.admin_add_incoming.auth_password = "pass42",
-      .details.admin_add_incoming.amount = "EUR:5.01" },
-    /* Run wirewatch to observe /admin/add/incoming */
-    { .oc = OC_RUN_WIREWATCH,
-      .label = "wirewatch-4" },
-    /* Withdraw a 5 EUR coin, at fee of 1 ct */
-    { .oc = OC_WITHDRAW_SIGN,
-      .label = "payback-withdraw-coin-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.reserve_withdraw.reserve_reference = "payback-create-reserve-1",
-      .details.reserve_withdraw.amount = "EUR:5" },
-    { .oc = OC_REVOKE,
-      .label = "revoke-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.revoke.ref = "payback-withdraw-coin-1" },
-    { .oc = OC_PAYBACK,
-      .label = "payback-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.payback.ref = "payback-withdraw-coin-1",
-      .details.payback.amount = "EUR:5" },
-    /* Check the money is back with the reserve */
-    { .oc = OC_WITHDRAW_STATUS,
-      .label = "payback-reserve-status-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.reserve_status.reserve_reference = "payback-create-reserve-1",
-      .details.reserve_status.expected_balance = "EUR:5.00" },
-
-
-    /* Fill reserve with EUR:2.02, as withdraw fee is 1 ct per config,
-       then withdraw two coin, partially spend one, and then have the rest 
paid back.
-       Check deposit of other coin fails.
-       (Do not use EUR:5 here as the EUR:5 coin was revoked and we did not
-       bother to create a new one...) */
-    { .oc = OC_ADMIN_ADD_INCOMING,
-      .label = "payback-create-reserve-2",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.debit_account_no = 42,
-      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
-      .details.admin_add_incoming.auth_username = "user42",
-      .details.admin_add_incoming.auth_password = "pass42",
-      .details.admin_add_incoming.amount = "EUR:2.02" },
-    /* Run wirewatch to observe /admin/add/incoming */
-    { .oc = OC_RUN_WIREWATCH,
-      .label = "wirewatch-5" },
-    /* Withdraw a 1 EUR coin, at fee of 1 ct */
-    { .oc = OC_WITHDRAW_SIGN,
-      .label = "payback-withdraw-coin-2a",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.reserve_withdraw.reserve_reference = "payback-create-reserve-2",
-      .details.reserve_withdraw.amount = "EUR:1" },
-    /* Withdraw a 1 EUR coin, at fee of 1 ct */
-    { .oc = OC_WITHDRAW_SIGN,
-      .label = "payback-withdraw-coin-2b",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.reserve_withdraw.reserve_reference = "payback-create-reserve-2",
-      .details.reserve_withdraw.amount = "EUR:1" },
-
-    { .oc = OC_DEPOSIT,
-      .label = "payback-deposit-partial",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.deposit.amount = "EUR:0.5",
-      .details.deposit.coin_ref = "payback-withdraw-coin-2a",
-      .details.deposit.wire_details = "{ 
\"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\"    }",
-      .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"more ice 
cream\", \"value\":1 } ] }" },
-    { .oc = OC_REVOKE,
-      .label = "revoke-2",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.revoke.ref = "payback-withdraw-coin-2a" },
-    { .oc = OC_PAYBACK,
-      .label = "payback-2",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.payback.ref = "payback-withdraw-coin-2a",
-      .details.payback.amount = "EUR:0.5" },
-    { .oc = OC_PAYBACK,
-      .label = "payback-2b",
-      .expected_response_code = MHD_HTTP_FORBIDDEN,
-      .details.payback.ref = "payback-withdraw-coin-2a",
-      .details.payback.amount = "EUR:0.5" },
-    { .oc = OC_DEPOSIT,
-      .label = "payback-deposit-revoked",
-      .expected_response_code = MHD_HTTP_NOT_FOUND,
-      .details.deposit.amount = "EUR:1",
-      .details.deposit.coin_ref = "payback-withdraw-coin-2b",
-      .details.deposit.wire_details = "{ 
\"url\":\"payto://x-taler-bank/localhost:8082/42\",   \"salt\":\"my salt\"  }",
-      .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"more ice 
cream\", \"value\":1 } ] }" },
-
-    /* Test deposit fails after payback, with proof in payback */
-    /* FIXME: #3887: right now, the exchange will never return the
-       coin's transaction history with payback data, as we get a 404 on the 
DK! */
-    { .oc = OC_DEPOSIT,
-      .label = "payback-deposit-partial-after-payback",
-      .expected_response_code = MHD_HTTP_NOT_FOUND,
-      .details.deposit.amount = "EUR:0.5",
-      .details.deposit.coin_ref = "payback-withdraw-coin-2a",
-      .details.deposit.wire_details = "{ 
\"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\"  }",
-      .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"extra ice 
cream\", \"value\":1 } ] }" },
-
-
-    /* Test that revoked coins cannot be withdrawn */
-    { .oc = OC_ADMIN_ADD_INCOMING,
-      .label = "payback-create-reserve-3",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.debit_account_no = 42,
-      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
-      .details.admin_add_incoming.auth_username = "user42",
-      .details.admin_add_incoming.auth_password = "pass42",
-      .details.admin_add_incoming.amount = "EUR:1.01" },
-    /* Run wirewatch to observe /admin/add/incoming */
-    { .oc = OC_RUN_WIREWATCH,
-      .label = "wirewatch-6" },
-    { .oc = OC_WITHDRAW_SIGN,
-      .label = "payback-withdraw-coin-3-revoked",
-      .expected_response_code = MHD_HTTP_NOT_FOUND,
-      .details.reserve_withdraw.reserve_reference = "payback-create-reserve-3",
-      .details.reserve_withdraw.amount = "EUR:1" },
-
-    /* check that we are empty before the rejection test */
-    { .oc = OC_CHECK_BANK_TRANSFER,
-      .label = "check_bank_transfer-pr1",
-      .details.check_bank_transfer.exchange_base_url = 
"http://localhost:8081/";,
-      .details.check_bank_transfer.amount = "EUR:5.01",
-      .details.check_bank_transfer.account_debit = 42,
-      .details.check_bank_transfer.account_credit = 2
-    },
-    { .oc = OC_CHECK_BANK_TRANSFER,
-      .label = "check_bank_transfer-pr2",
-      .details.check_bank_transfer.exchange_base_url = 
"http://localhost:8081/";,
-      .details.check_bank_transfer.amount = "EUR:2.02",
-      .details.check_bank_transfer.account_debit = 42,
-      .details.check_bank_transfer.account_credit = 2
-    },
-    { .oc = OC_CHECK_BANK_TRANSFER,
-      .label = "check_bank_transfer-pr3",
-      .details.check_bank_transfer.exchange_base_url = 
"http://localhost:8081/";,
-      .details.check_bank_transfer.amount = "EUR:1.01",
-      .details.check_bank_transfer.account_debit = 42,
-      .details.check_bank_transfer.account_credit = 2
-    },
-
-    { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
-      .label = "check-empty-again" },
-
-    /* Test rejection of bogus wire transfers */
-    { .oc = OC_ADMIN_ADD_INCOMING,
-      .label = "bogus-subject",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.subject = "not a reserve public key",
-      .details.admin_add_incoming.debit_account_no = 42,
-      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
-      .details.admin_add_incoming.auth_username = "user42",
-      .details.admin_add_incoming.auth_password = "pass42",
-      .details.admin_add_incoming.amount = "EUR:1.01" },
-    /* Run wirewatch to observe rejection */
-    { .oc = OC_RUN_WIREWATCH,
-      .label = "wirewatch-7" },
-    { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
-      .label = "check-empty-from-reject" },
-
-
-    /* ************** End of payback API testing************* */
-#endif
-
-    { .oc = OC_END }
-  };
-
-  is = GNUNET_new (struct InterpreterState);
-  is->commands = commands;
-
-  ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
-                          &rc);
-  GNUNET_assert (NULL != ctx);
-  rc = GNUNET_CURL_gnunet_rc_create (ctx);
-  fakebank = TALER_FAKEBANK_start (8082);
-  exchange = TALER_EXCHANGE_connect (ctx,
-                                     "http://localhost:8081";,
-                                     &cert_cb,
-                                     is,
-                                     TALER_EXCHANGE_OPTION_END);
-  GNUNET_assert (NULL != exchange);
-  timeout_task
-    = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
-                                    (GNUNET_TIME_UNIT_SECONDS, 300),
-                                    &do_timeout, NULL);
-  GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is);
-}
-
-
-/**
- * Remove files from previous runs
- */
-static void
-cleanup_files ()
-{
-  struct GNUNET_CONFIGURATION_Handle *cfg;
-  char *dir;
-
-  cfg = GNUNET_CONFIGURATION_create ();
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_load (cfg,
-                                 "test_exchange_api.conf"))
-  {
-    GNUNET_break (0);
-    GNUNET_CONFIGURATION_destroy (cfg);
-    exit (77);
-  }
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CONFIGURATION_get_value_filename (cfg,
-                                                          "exchange",
-                                                          "keydir",
-                                                          &dir));
-  if (GNUNET_YES ==
-      GNUNET_DISK_directory_test (dir,
-                                  GNUNET_NO))
-    GNUNET_break (GNUNET_OK ==
-                  GNUNET_DISK_directory_remove (dir));
-  GNUNET_free (dir);
-  GNUNET_CONFIGURATION_destroy (cfg);
-}
-
-
-/**
- * Main function for the testcase for the exchange API.
- *
- * @param argc expected to be 1
- * @param argv expected to only contain the program name
- */
-int
-main (int argc,
-      char * const *argv)
-{
-  struct GNUNET_OS_Process *proc;
-  struct GNUNET_SIGNAL_Context *shc_chld;
-  enum GNUNET_OS_ProcessStatusType type;
-  unsigned long code;
-  unsigned int iter;
-
-  /* These might get in the way... */
-  unsetenv ("XDG_DATA_HOME");
-  unsetenv ("XDG_CONFIG_HOME");
-  GNUNET_log_setup ("test-exchange-api",
-                    "INFO",
-                    NULL);
-  if (GNUNET_OK !=
-      GNUNET_NETWORK_test_port_free (IPPROTO_TCP,
-                                    8081))
-  {
-    fprintf (stderr,
-             "Required port %u not available, skipping.\n",
-            8081);
-    return 77;
-  }
-  if (GNUNET_OK !=
-      GNUNET_NETWORK_test_port_free (IPPROTO_TCP,
-                                    8082))
-  {
-    fprintf (stderr,
-             "Required port %u not available, skipping.\n",
-            8082);
-    return 77;
-  }
-  cleanup_files ();
-
-  proc = GNUNET_OS_start_process (GNUNET_NO,
-                                  GNUNET_OS_INHERIT_STD_ALL,
-                                  NULL, NULL, NULL,
-                                  "taler-exchange-keyup",
-                                  "taler-exchange-keyup",
-                                  "-c", "test_exchange_api.conf",
-                                  "-o", "auditor.in",
-                                  NULL);
-  if (NULL == proc)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               "Failed to run `taler-exchange-keyup`, is your PATH 
correct?\n");
-    return 77;
-  }
-  GNUNET_OS_process_wait (proc);
-  GNUNET_OS_process_destroy (proc);
-
-  proc = GNUNET_OS_start_process (GNUNET_NO,
-                                  GNUNET_OS_INHERIT_STD_ALL,
-                                  NULL, NULL, NULL,
-                                  "taler-auditor-sign",
-                                  "taler-auditor-sign",
-                                  "-c", "test_exchange_api.conf",
-                                  "-u", "http://auditor/";,
-                                  "-m", 
"98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG",
-                                  "-r", "auditor.in",
-                                  "-o", 
"test_exchange_api_home/.local/share/taler/auditors/auditor.out",
-                                  NULL);
-  if (NULL == proc)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               "Failed to run `taler-exchange-keyup`, is your PATH 
correct?\n");
-    return 77;
-  }
-  GNUNET_OS_process_wait (proc);
-  GNUNET_OS_process_destroy (proc);
-
-  proc = GNUNET_OS_start_process (GNUNET_NO,
-                                  GNUNET_OS_INHERIT_STD_ALL,
-                                  NULL, NULL, NULL,
-                                  "taler-exchange-dbinit",
-                                  "taler-exchange-dbinit",
-                                  "-c", "test_exchange_api.conf",
-                                  "-r",
-                                  NULL);
-  if (NULL == proc)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               "Failed to run `taler-exchange-dbinit`, is your PATH 
correct?\n");
-    return 77;
-  }
-  if (GNUNET_SYSERR ==
-      GNUNET_OS_process_wait_status (proc,
-                                     &type,
-                                     &code))
-  {
-    GNUNET_break (0);
-    GNUNET_OS_process_destroy (proc);
-    return 1;
-  }
-  GNUNET_OS_process_destroy (proc);
-  if ( (type == GNUNET_OS_PROCESS_EXITED) &&
-       (0 != code) )
-  {
-    fprintf (stderr,
-             "Failed to setup database\n");
-    return 77;
-  }
-  if ( (type != GNUNET_OS_PROCESS_EXITED) ||
-       (0 != code) )
-  {
-    fprintf (stderr,
-             "Unexpected error running `taler-exchange-dbinit'!\n");
-    return 1;
-  }
-  exchanged = GNUNET_OS_start_process (GNUNET_NO,
-                                       GNUNET_OS_INHERIT_STD_ALL,
-                                       NULL, NULL, NULL,
-                                       "taler-exchange-httpd",
-                                       "taler-exchange-httpd",
-                                       "-c", "test_exchange_api.conf",
-                                       "-i",
-                                       NULL);
-  /* give child time to start and bind against the socket */
-  fprintf (stderr,
-           "Waiting for `taler-exchange-httpd' to be ready");
-  iter = 0;
-  do
-    {
-      if (10 == iter)
-      {
-       fprintf (stderr,
-                "Failed to launch `taler-exchange-httpd' (or `wget')\n");
-       GNUNET_OS_process_kill (exchanged,
-                               SIGTERM);
-       GNUNET_OS_process_wait (exchanged);
-       GNUNET_OS_process_destroy (exchanged);
-       return 77;
-      }
-      fprintf (stderr, ".");
-      sleep (1);
-      iter++;
-    }
-  while (0 != system ("wget -q -t 1 -T 1 http://127.0.0.1:8081/keys -o 
/dev/null -O /dev/null"));
-  fprintf (stderr, "\n");
-
-  result = GNUNET_NO;
-  sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO);
-  GNUNET_assert (NULL != sigpipe);
-  shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD,
-                                            &sighandler_child_death);
-  GNUNET_SCHEDULER_run (&run, NULL);
-  GNUNET_SIGNAL_handler_uninstall (shc_chld);
-  shc_chld = NULL;
-  GNUNET_DISK_pipe_close (sigpipe);
-  GNUNET_break (0 ==
-                GNUNET_OS_process_kill (exchanged,
-                                        SIGTERM));
-  GNUNET_break (GNUNET_OK ==
-                GNUNET_OS_process_wait (exchanged));
-  GNUNET_OS_process_destroy (exchanged);
-  return (GNUNET_OK == result) ? 0 : 1;
-}
-
-/* end of test_exchange_api.c */
diff --git a/src/exchange-lib/test_exchange_api_keys_cherry_picking.c 
b/src/exchange-lib/test_exchange_api_keys_cherry_picking.c
deleted file mode 100644
index 0dc0c540..00000000
--- a/src/exchange-lib/test_exchange_api_keys_cherry_picking.c
+++ /dev/null
@@ -1,803 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014-2017 GNUnet e.V. and Inria
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along with
-  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file exchange/test_exchange_api_keys_cherry_picking.c
- * @brief testcase to test exchange's /keys cherry picking ability
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "taler_util.h"
-#include "taler_signatures.h"
-#include "taler_exchange_service.h"
-#include "taler_json_lib.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <microhttpd.h>
-
-
-/**
- * Main execution context for the main loop.
- */
-static struct GNUNET_CURL_Context *ctx;
-
-/**
- * Handle to access the exchange.
- */
-static struct TALER_EXCHANGE_Handle *exchange;
-
-/**
- * Context for running the CURL event loop.
- */
-static struct GNUNET_CURL_RescheduleContext *rc;
-
-/**
- * Handle to the exchange process.
- */
-static struct GNUNET_OS_Process *exchanged;
-
-/**
- * Task run on timeout.
- */
-static struct GNUNET_SCHEDULER_Task *timeout_task;
-
-/**
- * Result of the testcases, #GNUNET_OK on success
- */
-static int result;
-
-
-/**
- * Opcodes for the interpreter.
- */
-enum OpCode
-{
-  /**
-   * Termination code, stops the interpreter loop (with success).
-   */
-  OC_END = 0,
-
-  /**
-   * Run a process.
-   */
-  OC_RUN_PROCESS,
-
-  /**
-   * Signal the exchange to reload the keys.
-   */
-  OC_SIGNAL_EXCHANGE,
-
-  /**
-   * Check the /keys.
-   */
-  OC_CHECK_KEYS
-
-};
-
-
-/**
- * Details for a exchange operation to execute.
- */
-struct Command
-{
-  /**
-   * Opcode of the command.
-   */
-  enum OpCode oc;
-
-  /**
-   * Label for the command, can be NULL.
-   */
-  const char *label;
-
-  /**
-   * Details about the command.
-   */
-  union
-  {
-
-    struct {
-
-      /**
-       * Binary to execute.
-       */
-      const char *binary;
-
-      /**
-       * Command-line arguments for the process to be run.
-       */
-      char *const *argv;
-
-      /**
-       * Process handle.
-       */
-      struct GNUNET_OS_Process *proc;
-
-      /**
-       * ID of task called whenever we get a SIGCHILD.
-       */
-      struct GNUNET_SCHEDULER_Task *child_death_task;
-
-    } run_process;
-
-    struct {
-
-      /**
-       * Expected number of denomination keys.
-       */
-      unsigned int num_denom_keys;
-
-      /**
-       * Which generation of /keys are we verifying here?
-       * Used to make sure we got the right number of
-       * interactions.
-       */
-      unsigned int generation;
-
-    } check_keys;
-
-  } details;
-
-};
-
-
-/**
- * State of the interpreter loop.
- */
-struct InterpreterState
-{
-  /**
-   * Keys from the exchange.
-   */
-  const struct TALER_EXCHANGE_Keys *keys;
-
-  /**
-   * Commands the interpreter will run.
-   */
-  struct Command *commands;
-
-  /**
-   * Interpreter task (if one is scheduled).
-   */
-  struct GNUNET_SCHEDULER_Task *task;
-
-  /**
-   * Instruction pointer.  Tells #interpreter_run() which
-   * instruction to run next.
-   */
-  unsigned int ip;
-
-  /**
-   * Is the interpreter running (#GNUNET_YES) or waiting
-   * for /keys (#GNUNET_NO)?
-   */
-  int working;
-
-  /**
-   * How often have we gotten a /keys response so far?
-   */
-  unsigned int key_generation;
-
-};
-
-
-/**
- * Pipe used to communicate child death via signal.
- */
-static struct GNUNET_DISK_PipeHandle *sigpipe;
-
-
-/**
- * The testcase failed, return with an error code.
- *
- * @param is interpreter state to clean up
- */
-static void
-fail (struct InterpreterState *is)
-{
-  result = GNUNET_SYSERR;
-  GNUNET_SCHEDULER_shutdown ();
-}
-
-
-/**
- * Run the main interpreter loop that performs exchange operations.
- *
- * @param cls contains the `struct InterpreterState`
- */
-static void
-interpreter_run (void *cls);
-
-
-/**
- * Run the next command with the interpreter.
- *
- * @param is current interpeter state.
- */
-static void
-next_command (struct InterpreterState *is)
-{
-  if (GNUNET_SYSERR == result)
-    return; /* ignore, we already failed! */
-  is->ip++;
-  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
-                                       is);
-}
-
-
-/**
- * Task triggered whenever we receive a SIGCHLD (child
- * process died).
- *
- * @param cls closure, NULL if we need to self-restart
- */
-static void
-maint_child_death (void *cls)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-  const struct GNUNET_DISK_FileHandle *pr;
-  char c[16];
-
-  switch (cmd->oc) {
-  case OC_RUN_PROCESS:
-    cmd->details.run_process.child_death_task = NULL;
-    pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
-    GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
-    GNUNET_OS_process_wait (cmd->details.run_process.proc);
-    GNUNET_OS_process_destroy (cmd->details.run_process.proc);
-    cmd->details.run_process.proc = NULL;
-    break;
-  default:
-    GNUNET_break (0);
-    fail (is);
-    return;
-  }
-  next_command (is);
-}
-
-
-/**
- * Run the main interpreter loop that performs exchange operations.
- *
- * @param cls contains the `struct InterpreterState`
- */
-static void
-interpreter_run (void *cls)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-  const struct GNUNET_SCHEDULER_TaskContext *tc;
-
-  is->task = NULL;
-  tc = GNUNET_SCHEDULER_get_task_context ();
-  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
-  {
-    fprintf (stderr,
-             "Test aborted by shutdown request\n");
-    fail (is);
-    return;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Running command `%s'\n",
-              cmd->label);
-  switch (cmd->oc)
-  {
-  case OC_END:
-    result = GNUNET_OK;
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  case OC_RUN_PROCESS:
-    {
-      const struct GNUNET_DISK_FileHandle *pr;
-
-      cmd->details.run_process.proc
-        = GNUNET_OS_start_process_vap (GNUNET_NO,
-                                       GNUNET_OS_INHERIT_STD_ALL,
-                                       NULL, NULL, NULL,
-                                       cmd->details.run_process.binary,
-                                       cmd->details.run_process.argv);
-      if (NULL == cmd->details.run_process.proc)
-      {
-        GNUNET_break (0);
-        fail (is);
-        return;
-      }
-      pr = GNUNET_DISK_pipe_handle (sigpipe,
-                                    GNUNET_DISK_PIPE_END_READ);
-      cmd->details.run_process.child_death_task
-        = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
-                                          pr,
-                                          &maint_child_death,
-                                          is);
-      return;
-    }
-  case OC_SIGNAL_EXCHANGE:
-    {
-      GNUNET_break (0 ==
-                    GNUNET_OS_process_kill (exchanged,
-                                            SIGUSR1));
-      /* give exchange time to process the signal */
-      sleep (1);
-      next_command (is);
-      return;
-    }
-  case OC_CHECK_KEYS:
-    {
-      if (is->key_generation < cmd->details.check_keys.generation)
-      {
-        /* Go back to waiting for /keys signal! */
-        is->working = GNUNET_NO;
-        GNUNET_break (0 ==
-                      TALER_EXCHANGE_check_keys_current (exchange,
-                                                         
GNUNET_YES).abs_value_us);
-        return;
-      }
-      if (is->key_generation > cmd->details.check_keys.generation)
-      {
-        /* We got /keys too often, strange. Fatal. May theoretically happen if
-           somehow we were really unlucky and /keys expired "naturally", but
-           obviously with a sane configuration this should also not be. */
-        GNUNET_break (0);
-        fail (is);
-        return;
-      }
-      /* /keys was updated, let's check they were OK! */
-      if (cmd->details.check_keys.num_denom_keys !=
-          is->keys->num_denom_keys)
-      {
-        /* Did not get the expected number of denomination keys! */
-        GNUNET_break (0);
-        fprintf (stderr,
-                 "Got %u keys in step %s\n",
-                 is->keys->num_denom_keys,
-                 cmd->label);
-        fail (is);
-        return;
-      }
-      next_command (is);
-      return;
-    }
-  default:
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unknown instruction %d at %u (%s)\n",
-                cmd->oc,
-                is->ip,
-                cmd->label);
-    fail (is);
-    return;
-  }
-}
-
-
-/**
- * Signal handler called for SIGCHLD.  Triggers the
- * respective handler by writing to the trigger pipe.
- */
-static void
-sighandler_child_death ()
-{
-  static char c;
-  int old_errno = errno;       /* back-up errno */
-
-  GNUNET_break (1 ==
-               GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
-                                       (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
-                                       &c, sizeof (c)));
-  errno = old_errno;           /* restore errno */
-}
-
-
-/**
- * Function run when the test terminates (good or bad) with timeout.
- *
- * @param cls NULL
- */
-static void
-do_timeout (void *cls)
-{
-  timeout_task = NULL;
-  GNUNET_SCHEDULER_shutdown ();
-}
-
-
-/**
- * Function run when the test terminates (good or bad).
- * Cleans up our state.
- *
- * @param cls the interpreter state.
- */
-static void
-do_shutdown (void *cls)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd;
-
-  for (unsigned int i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
-  {
-    switch (cmd->oc)
-    {
-    case OC_END:
-      GNUNET_assert (0);
-      break;
-    case OC_RUN_PROCESS:
-      if (NULL != cmd->details.run_process.proc)
-      {
-        GNUNET_break (0 ==
-                      GNUNET_OS_process_kill (cmd->details.run_process.proc,
-                                              SIGKILL));
-        GNUNET_OS_process_wait (cmd->details.run_process.proc);
-        GNUNET_OS_process_destroy (cmd->details.run_process.proc);
-        cmd->details.run_process.proc = NULL;
-      }
-      if (NULL != cmd->details.run_process.child_death_task)
-      {
-        GNUNET_SCHEDULER_cancel (cmd->details.run_process.child_death_task);
-        cmd->details.run_process.child_death_task = NULL;
-      }
-      break;
-    case OC_SIGNAL_EXCHANGE:
-      /* nothing to do */
-      break;
-    case OC_CHECK_KEYS:
-      /* nothing to do */
-      break;
-    default:
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Unknown instruction %d at %u (%s)\n",
-                  cmd->oc,
-                  i,
-                  cmd->label);
-      break;
-    }
-  }
-  if (NULL != is->task)
-  {
-    GNUNET_SCHEDULER_cancel (is->task);
-    is->task = NULL;
-  }
-  GNUNET_free (is);
-  if (NULL != exchange)
-  {
-    TALER_EXCHANGE_disconnect (exchange);
-    exchange = NULL;
-  }
-  if (NULL != ctx)
-  {
-    GNUNET_CURL_fini (ctx);
-    ctx = NULL;
-  }
-  if (NULL != rc)
-  {
-    GNUNET_CURL_gnunet_rc_destroy (rc);
-    rc = NULL;
-  }
-  if (NULL != timeout_task)
-  {
-    GNUNET_SCHEDULER_cancel (timeout_task);
-    timeout_task = NULL;
-  }
-}
-
-
-/**
- * Functions of this type are called to provide the retrieved signing and
- * denomination keys of the exchange.  No TALER_EXCHANGE_*() functions should 
be called
- * in this callback.
- *
- * @param cls closure
- * @param keys information about keys of the exchange
- * @param vc version compatibility
- */
-static void
-cert_cb (void *cls,
-         const struct TALER_EXCHANGE_Keys *keys,
-        enum TALER_EXCHANGE_VersionCompatibility vc)
-{
-  struct InterpreterState *is = cls;
-
-  /* check that keys is OK */
-#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); 
GNUNET_SCHEDULER_shutdown(); return; } while (0)
-  ERR (NULL == keys);
-  ERR (0 == keys->num_sign_keys);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Read %u signing keys\n",
-              keys->num_sign_keys);
-  ERR (0 == keys->num_denom_keys);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Read %u denomination keys\n",
-              keys->num_denom_keys);
-#undef ERR
-
-  /* run actual tests via interpreter-loop */
-  is->keys = keys;
-  if (GNUNET_YES == is->working)
-    return;
-  is->working = GNUNET_YES;
-  is->key_generation++;
-  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
-                                       is);
-}
-
-
-/**
- * Main function that will be run by the scheduler.
- *
- * @param cls closure
- */
-static void
-run (void *cls)
-{
-  struct InterpreterState *is;
-  static char *keyup[] = {
-    "taler-exchange-keyup",
-    "-c", "test_exchange_api_keys_cherry_picking_extended.conf",
-    "-o", "auditor.in",
-    NULL
-  };
-  static char *auditorsign[] = {
-    "taler-auditor-sign",
-    "-c", "test_exchange_api_keys_cherry_picking.conf",
-    "-u", "http://auditor/";,
-    "-m", "98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG",
-    "-r", "auditor.in",
-    "-o", "test_exchange_api_home/.local/share/taler/auditors/auditor.out",
-    NULL
-  };
-  static struct Command commands[] =
-  {
-    /* Test signal handling by itself */
-    { .oc = OC_SIGNAL_EXCHANGE },
-    /* Check we got /keys properly */
-    { .oc = OC_CHECK_KEYS,
-      .details.check_keys.generation = 1,
-      .details.check_keys.num_denom_keys = 4
-    },
-    /* Generate more keys */
-    { .oc = OC_RUN_PROCESS,
-      .details.run_process.binary = "taler-exchange-keyup",
-      .details.run_process.argv = keyup
-    },
-    /* Auditor-sign them */
-    { .oc = OC_RUN_PROCESS,
-      .details.run_process.binary = "taler-auditor-sign",
-      .details.run_process.argv = auditorsign
-    },
-    /* Load new keys into exchange via signal */
-    { .oc = OC_SIGNAL_EXCHANGE },
-    /* Re-download and check /keys */
-    { .oc = OC_CHECK_KEYS,
-      .details.check_keys.generation = 2,
-#if TALER_EXCHANGE_API_DISABLE_CHERRYPICKING
-      .details.check_keys.num_denom_keys = 12
-#else
-      .details.check_keys.num_denom_keys = 8
-#endif
-    },
-    { .oc = OC_END }
-  };
-
-  is = GNUNET_new (struct InterpreterState);
-  is->commands = commands;
-
-  ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
-                          &rc);
-  GNUNET_assert (NULL != ctx);
-  rc = GNUNET_CURL_gnunet_rc_create (ctx);
-  exchange = TALER_EXCHANGE_connect (ctx,
-                                     "http://localhost:8081";,
-                                     &cert_cb,
-                                     is,
-                                     TALER_EXCHANGE_OPTION_END);
-  GNUNET_assert (NULL != exchange);
-  timeout_task
-    = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
-                                    (GNUNET_TIME_UNIT_SECONDS, 300),
-                                    &do_timeout,
-                                    NULL);
-  GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
-                                 is);
-}
-
-
-/**
- * Remove files from previous runs
- */
-static void
-cleanup_files ()
-{
-  struct GNUNET_CONFIGURATION_Handle *cfg;
-  char *dir;
-
-  cfg = GNUNET_CONFIGURATION_create ();
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_load (cfg,
-                                 "test_exchange_api.conf"))
-  {
-    GNUNET_break (0);
-    GNUNET_CONFIGURATION_destroy (cfg);
-    exit (77);
-  }
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CONFIGURATION_get_value_filename (cfg,
-                                                          "exchange",
-                                                          "keydir",
-                                                          &dir));
-  if (GNUNET_YES ==
-      GNUNET_DISK_directory_test (dir,
-                                  GNUNET_NO))
-    GNUNET_break (GNUNET_OK ==
-                  GNUNET_DISK_directory_remove (dir));
-  GNUNET_free (dir);
-  GNUNET_CONFIGURATION_destroy (cfg);
-}
-
-
-/**
- * Main function for the testcase for the exchange API.
- *
- * @param argc expected to be 1
- * @param argv expected to only contain the program name
- */
-int
-main (int argc,
-      char * const *argv)
-{
-  struct GNUNET_OS_Process *proc;
-  struct GNUNET_SIGNAL_Context *shc_chld;
-  enum GNUNET_OS_ProcessStatusType type;
-  unsigned long code;
-  unsigned int iter;
-
-  /* These might get in the way... */
-  unsetenv ("XDG_DATA_HOME");
-  unsetenv ("XDG_CONFIG_HOME");
-  GNUNET_log_setup ("test-exchange-api-keys-cherry-picking",
-                    "INFO",
-                    NULL);
-  if (GNUNET_OK !=
-      GNUNET_NETWORK_test_port_free (IPPROTO_TCP,
-                                    8081))
-  {
-    fprintf (stderr,
-             "Required port %u not available, skipping.\n",
-            8081);
-    return 77;
-  }
-  cleanup_files ();
-
-  proc = GNUNET_OS_start_process (GNUNET_NO,
-                                  GNUNET_OS_INHERIT_STD_ALL,
-                                  NULL, NULL, NULL,
-                                  "taler-exchange-keyup",
-                                  "taler-exchange-keyup",
-                                  "-c", 
"test_exchange_api_keys_cherry_picking.conf",
-                                  "-o", "auditor.in",
-                                  NULL);
-  if (NULL == proc)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               "Failed to run `taler-exchange-keyup`, is your PATH 
correct?\n");
-    return 77;
-  }
-  GNUNET_OS_process_wait (proc);
-  GNUNET_OS_process_destroy (proc);
-
-  proc = GNUNET_OS_start_process (GNUNET_NO,
-                                  GNUNET_OS_INHERIT_STD_ALL,
-                                  NULL, NULL, NULL,
-                                  "taler-auditor-sign",
-                                  "taler-auditor-sign",
-                                  "-c", 
"test_exchange_api_keys_cherry_picking.conf",
-                                  "-u", "http://auditor/";,
-                                  "-m", 
"98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG",
-                                  "-r", "auditor.in",
-                                  "-o", 
"test_exchange_api_home/.local/share/taler/auditors/auditor.out",
-                                  NULL);
-  if (NULL == proc)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               "Failed to run `taler-exchange-keyup`, is your PATH 
correct?\n");
-    return 77;
-  }
-  GNUNET_OS_process_wait (proc);
-  GNUNET_OS_process_destroy (proc);
-
-  proc = GNUNET_OS_start_process (GNUNET_NO,
-                                  GNUNET_OS_INHERIT_STD_ALL,
-                                  NULL, NULL, NULL,
-                                  "taler-exchange-dbinit",
-                                  "taler-exchange-dbinit",
-                                  "-c", 
"test_exchange_api_keys_cherry_picking.conf",
-                                  "-r",
-                                  NULL);
-  if (NULL == proc)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               "Failed to run `taler-exchange-dbinit`, is your PATH 
correct?\n");
-    return 77;
-  }
-  if (GNUNET_SYSERR ==
-      GNUNET_OS_process_wait_status (proc,
-                                     &type,
-                                     &code))
-  {
-    GNUNET_break (0);
-    GNUNET_OS_process_destroy (proc);
-    return 1;
-  }
-  GNUNET_OS_process_destroy (proc);
-  if ( (type == GNUNET_OS_PROCESS_EXITED) &&
-       (0 != code) )
-  {
-    fprintf (stderr,
-             "Failed to setup database\n");
-    return 77;
-  }
-  if ( (type != GNUNET_OS_PROCESS_EXITED) ||
-       (0 != code) )
-  {
-    fprintf (stderr,
-             "Unexpected error running `taler-exchange-dbinit'!\n");
-    return 1;
-  }
-  exchanged = GNUNET_OS_start_process (GNUNET_NO,
-                                       GNUNET_OS_INHERIT_STD_ALL,
-                                       NULL, NULL, NULL,
-                                       "taler-exchange-httpd",
-                                       "taler-exchange-httpd",
-                                       "-c", 
"test_exchange_api_keys_cherry_picking.conf",
-                                       "-i",
-                                       NULL);
-  /* give child time to start and bind against the socket */
-  fprintf (stderr,
-           "Waiting for `taler-exchange-httpd' to be ready");
-  iter = 0;
-  do
-    {
-      if (10 == iter)
-      {
-       fprintf (stderr,
-                "Failed to launch `taler-exchange-httpd' (or `wget')\n");
-       GNUNET_OS_process_kill (exchanged,
-                               SIGTERM);
-       GNUNET_OS_process_wait (exchanged);
-       GNUNET_OS_process_destroy (exchanged);
-       return 77;
-      }
-      fprintf (stderr, ".");
-      sleep (1);
-      iter++;
-    }
-  while (0 != system ("wget -q -t 1 -T 1 http://127.0.0.1:8081/keys -o 
/dev/null -O /dev/null"));
-  fprintf (stderr, "\n");
-  result = GNUNET_NO;
-  sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO);
-  GNUNET_assert (NULL != sigpipe);
-  shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD,
-                                            &sighandler_child_death);
-  GNUNET_SCHEDULER_run (&run, NULL);
-  GNUNET_SIGNAL_handler_uninstall (shc_chld);
-  shc_chld = NULL;
-  GNUNET_DISK_pipe_close (sigpipe);
-  GNUNET_break (0 ==
-                GNUNET_OS_process_kill (exchanged,
-                                        SIGTERM));
-  GNUNET_break (GNUNET_OK ==
-                GNUNET_OS_process_wait (exchanged));
-  GNUNET_OS_process_destroy (exchanged);
-  return (GNUNET_OK == result) ? 0 : 1;
-}
-
-/* end of test_exchange_api_keys_cherry_picking.c */
diff --git a/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf 
b/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf
index ba82c1c3..212e03d6 100644
--- a/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf
+++ b/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf
@@ -11,14 +11,14 @@ CURRENCY = EUR
 [exchange]
 
 # how long is one signkey valid?
-signkey_duration = 4 weeks
+signkey_duration = 5 seconds
 
 # how long are the signatures with the signkey valid?
 legal_duration = 2 years
 
 # how long do we provide to clients denomination and signing keys
 # ahead of time?
-lookahead_provide = 4 weeks 1 day
+lookahead_provide = 30 seconds
 
 # Keep it short so we can prolong later!
 LOOKAHEAD_SIGN = 60 s
@@ -132,8 +132,8 @@ CLOSING-FEE-2026 = EUR:0.01
 
 [coin_eur_ct_1]
 value = EUR:0.01
-duration_overlap = 5 s
-duration_withdraw = 35 s
+duration_overlap = 1 s
+duration_withdraw = 25 s
 duration_spend = 40 s
 duration_legal = 60 s
 fee_withdraw = EUR:0.00
@@ -144,8 +144,8 @@ rsa_keysize = 1024
 
 [coin_eur_ct_2]
 value = EUR:0.02
-duration_overlap = 5 s
-duration_withdraw = 35 s
+duration_overlap = 1 s
+duration_withdraw = 25 s
 duration_spend = 40 s
 duration_legal = 60 s
 fee_withdraw = EUR:0.01
diff --git a/src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c 
b/src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c
index d1fd25cf..8c82f638 100644
--- a/src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c
+++ b/src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c
@@ -71,6 +71,9 @@ run (void *cls,
                                   1,
                                   4,
                                   is->exchange),
+    /* sleep a bit */
+    TALER_TESTING_cmd_sleep ("sleep",
+                             25),
     /* 1st keyup happens at start-up */
     TALER_TESTING_cmd_exec_keyup ("keyup-2",
                                   CONFIG_FILE_EXTENDED),
@@ -81,11 +84,23 @@ run (void *cls,
                               SIGUSR1),
     TALER_TESTING_cmd_check_keys ("check-keys-2",
                                   2,
-#if TALER_EXCHANGE_API_DISABLE_CHERRYPICKING
-                                  12,
-#else
-                                  8,
-#endif
+                                  6,
+                                  is->exchange),
+
+    /* sleep a bit */
+    TALER_TESTING_cmd_sleep ("sleep",
+                             24),
+    /* Do 2nd keyup */
+    TALER_TESTING_cmd_exec_keyup ("keyup-3",
+                                  CONFIG_FILE_EXTENDED),
+    TALER_TESTING_cmd_exec_auditor_sign ("sign-keys-2",
+                                         CONFIG_FILE),
+    TALER_TESTING_cmd_signal ("trigger-keys-reload-2",
+                              is->exchanged,
+                              SIGUSR1),
+    TALER_TESTING_cmd_check_keys ("check-keys-3",
+                                  3,
+                                  10,
                                   is->exchange),
     TALER_TESTING_cmd_end ()
   };
diff --git a/src/exchange-lib/testing_api_cmd_check_keys.c 
b/src/exchange-lib/testing_api_cmd_check_keys.c
index 56b062a1..8d91d3ec 100644
--- a/src/exchange-lib/testing_api_cmd_check_keys.c
+++ b/src/exchange-lib/testing_api_cmd_check_keys.c
@@ -71,8 +71,8 @@ check_keys_run (void *cls,
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "cmd `%s', key generation: %d\n",
-              cmd->label, is->key_generation);
-
+              cmd->label,
+              is->key_generation);
   if (is->key_generation < cks->generation)
   {
     /* Go back to waiting for /keys signal! */
@@ -99,10 +99,11 @@ check_keys_run (void *cls,
   {
     /* Did not get the expected number of denomination keys! */
     GNUNET_break (0);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Got %u keys in step %s\n",
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Got %u keys in step %s, expected %u\n",
                 is->keys->num_denom_keys,
-                cmd->label);
+                cmd->label,
+                cks->num_denom_keys);
     TALER_TESTING_interpreter_fail (is);
     return;
   }
diff --git a/src/exchange-tools/taler-exchange-keyup.c 
b/src/exchange-tools/taler-exchange-keyup.c
index 9946eac4..22c72d5c 100644
--- a/src/exchange-tools/taler-exchange-keyup.c
+++ b/src/exchange-tools/taler-exchange-keyup.c
@@ -639,6 +639,15 @@ get_cointype_params (const char *ct,
     return GNUNET_SYSERR;
   }
   GNUNET_TIME_round_rel (&params->duration_overlap);
+  if (params->duration_overlap.rel_value_us >=
+      params->duration_withdraw.rel_value_us)
+  {
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                               ct,
+                               "duration_overlap",
+                               "duration_overlap must be smaller than 
duration_withdraw!");
+    return GNUNET_SYSERR;
+  }
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_number (kcfg,
                                              ct,
@@ -814,8 +823,9 @@ exchange_keys_update_cointype (void *cls,
   {
     dkf = get_cointype_file (&p,
                              p.anchor);
-    GNUNET_break (GNUNET_YES != GNUNET_DISK_file_test (dkf));
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+    GNUNET_break (GNUNET_YES !=
+                  GNUNET_DISK_file_test (dkf));
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Generating denomination key for type `%s', start %s at %s\n",
                 coin_alias,
                 GNUNET_STRINGS_absolute_time_to_string (p.anchor),
diff --git a/src/exchange/taler-exchange-httpd_keystate.c 
b/src/exchange/taler-exchange-httpd_keystate.c
index 4d1f8888..ce2eafa7 100644
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ b/src/exchange/taler-exchange-httpd_keystate.c
@@ -1192,6 +1192,10 @@ build_keys_response (const struct ResponseFactoryContext 
*rfc,
 
   krd->cherry_pick_date = cherry_pick_date;
 
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Creating /keys for cherry pick date %s\n",
+              GNUNET_STRINGS_absolute_time_to_string (cherry_pick_date));
+
   /* Initialize `rbc` */
   memset (&rbc,
           0,
@@ -2062,6 +2066,11 @@ TEH_KS_handler_keys (struct TEH_RequestHandler *rh,
                  sizeof (struct KeysResponseData),
                  &krd_search_comparator);
 
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Filtering /keys by cherry pick date %s found entry %u/%u\n",
+              GNUNET_STRINGS_absolute_time_to_string (last_issue_date),
+              (unsigned int) (krd - key_state->krd_array),
+              key_state->krd_array_length);
   if ( (NULL == krd) &&
        (key_state->krd_array_length > 0) )
   {
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 98223fa6..d48dd862 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -30,14 +30,6 @@
 
 /* *********************  /keys *********************** */
 
-
-/**
- * Temporarily disable cherrypicking until it's fixed.
- * See #5315.
- */
-#define TALER_EXCHANGE_API_DISABLE_CHERRYPICKING 1
-
-
 /**
  * List of possible options to be passed to
  * #TALER_EXCHANGE_connect().
@@ -170,11 +162,10 @@ struct TALER_EXCHANGE_AuditorInformation
 
   /**
    * Array of length @a num_denom_keys with the denomination
-   * keys audited by this auditor.  Note that the array
-   * elements point to the same locations as the entries
-   * in the key's main `denom_keys` array.
+   * keys audited by this auditor.  Offsets into the
+   * key's main `denom_keys` array.
    */
-  const struct TALER_EXCHANGE_DenomPublicKey **denom_keys;
+  unsigned int *denom_key_offsets;
 };
 
 

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]