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: Implement new trait


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] branch master updated: Implement new traits-based tests.
Date: Mon, 12 Feb 2018 16:20:47 +0100

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

marcello pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new fe6960c  Implement new traits-based tests.
fe6960c is described below

commit fe6960cce854cd4c665a27c4368e4397c8e7bcfb
Author: Marcello Stanisci <address@hidden>
AuthorDate: Tue Jan 23 10:28:24 2018 +0100

    Implement new traits-based tests.
---
 .gitignore                                         |   2 +
 src/exchange-lib/Makefile.am                       |  42 +-
 .../test_exchange_api_keys_cherry_picking.conf     |   2 +-
 .../test_exchange_api_keys_cherry_picking_new.c    | 135 +++
 src/exchange-lib/test_exchange_api_new.c           | 671 +++++++++++++-
 src/exchange-lib/testing_api_cmd_bank_check.c      | 268 ++++++
 src/exchange-lib/testing_api_cmd_check_keys.c      | 153 ++++
 src/exchange-lib/testing_api_cmd_deposit.c         | 448 ++++++++++
 src/exchange-lib/testing_api_cmd_exec_aggregator.c | 162 ++++
 .../testing_api_cmd_exec_auditor-sign.c            | 221 +++++
 src/exchange-lib/testing_api_cmd_exec_keyup.c      | 168 ++++
 src/exchange-lib/testing_api_cmd_exec_wirewatch.c  |   6 +-
 .../testing_api_cmd_fakebank_transfer.c            | 113 +--
 src/exchange-lib/testing_api_cmd_payback.c         | 491 ++++++++++
 src/exchange-lib/testing_api_cmd_refresh.c         | 993 +++++++++++++++++++++
 src/exchange-lib/testing_api_cmd_refund.c          | 295 ++++++
 src/exchange-lib/testing_api_cmd_signal.c          | 112 +++
 src/exchange-lib/testing_api_cmd_status.c          | 240 +++++
 src/exchange-lib/testing_api_cmd_track.c           | 819 +++++++++++++++++
 src/exchange-lib/testing_api_cmd_wire.c            | 267 ++++++
 src/exchange-lib/testing_api_cmd_withdraw.c        | 117 ++-
 src/exchange-lib/testing_api_helpers.c             | 156 +++-
 src/exchange-lib/testing_api_loop.c                | 334 ++++---
 src/exchange-lib/testing_api_trait_blinding_key.c  |   8 +-
 src/exchange-lib/testing_api_trait_coin_priv.c     |  38 +-
 src/exchange-lib/testing_api_trait_denom_pub.c     |  46 +-
 src/exchange-lib/testing_api_trait_denom_sig.c     |   8 +-
 src/exchange-lib/testing_api_trait_fresh_coin.c    |  74 ++
 src/exchange-lib/testing_api_trait_key_peer.c      |  82 ++
 src/exchange-lib/testing_api_trait_number.c        |  74 ++
 src/exchange-lib/testing_api_trait_process.c       |   8 +-
 src/exchange-lib/testing_api_trait_reserve_priv.c  |   8 +-
 src/exchange-lib/testing_api_trait_string.c        | 209 +++++
 src/exchange-lib/testing_api_trait_wtid.c          |  74 ++
 src/exchange-lib/testing_api_traits.c              |  54 +-
 src/exchange-tools/taler-exchange-keyup.c          |   3 +
 src/exchange/taler-exchange-httpd_keystate.c       |   4 +
 src/include/taler_testing_lib.h                    | 978 +++++++++++++++++---
 38 files changed, 7429 insertions(+), 454 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7bf23b4..0ec39fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,6 +36,7 @@ src/auditor/taler-auditor
 src/auditor/taler-auditor-sign
 src/bank-lib/test_bank_api
 src/bank-lib/test_bank_api_with_fakebank
+src/exchange-lib/test_exchange_api_new
 src/exchange-lib/test_exchange_api
 src/exchange-lib/test_exchange_api_home/.local/share/taler/exchange/live-keys/
 src/exchange-lib/test_exchange_api_home/.local/share/taler/exchange/wirefees/
@@ -86,6 +87,7 @@ doc/manual/manual.vr
 contrib/taler-exchange.tag
 doxygen-doc/
 src/exchange-lib/test_exchange_api_keys_cherry_picking
+src/exchange-lib/test_exchange_api_keys_cherry_picking_new
 src/auditor/taler-wire-auditor
 contrib/auditor-report.aux
 contrib/auditor-report.log
diff --git a/src/exchange-lib/Makefile.am b/src/exchange-lib/Makefile.am
index 64a5f96..cf80a1d 100644
--- a/src/exchange-lib/Makefile.am
+++ b/src/exchange-lib/Makefile.am
@@ -34,14 +34,26 @@ libtalerexchange_la_LIBADD = \
   -ljansson \
   $(XLIB)
 
-
 libtalertesting_la_LDFLAGS = \
   -version-info 0:0:0 \
   -no-undefined
 libtalertesting_la_SOURCES = \
+  testing_api_cmd_exec_aggregator.c \
   testing_api_cmd_exec_wirewatch.c \
+  testing_api_cmd_exec_keyup.c \
+  testing_api_cmd_exec_auditor-sign.c \
   testing_api_cmd_fakebank_transfer.c \
   testing_api_cmd_withdraw.c \
+  testing_api_cmd_wire.c \
+  testing_api_cmd_refund.c \
+  testing_api_cmd_status.c \
+  testing_api_cmd_deposit.c \
+  testing_api_cmd_refresh.c \
+  testing_api_cmd_track.c \
+  testing_api_cmd_bank_check.c \
+  testing_api_cmd_payback.c \
+  testing_api_cmd_signal.c \
+  testing_api_cmd_check_keys.c \
   testing_api_helpers.c \
   testing_api_loop.c \
   testing_api_traits.c \
@@ -50,7 +62,12 @@ libtalertesting_la_SOURCES = \
   testing_api_trait_denom_pub.c \
   testing_api_trait_denom_sig.c \
   testing_api_trait_process.c \
-  testing_api_trait_reserve_priv.c
+  testing_api_trait_reserve_priv.c \
+  testing_api_trait_number.c \
+  testing_api_trait_fresh_coin.c \
+  testing_api_trait_string.c \
+  testing_api_trait_key_peer.c \
+  testing_api_trait_wtid.c
 
 libtalertesting_la_LIBADD = \
   $(top_builddir)/src/json/libtalerjson.la \
@@ -71,13 +88,17 @@ endif
 endif
 
 check_PROGRAMS = \
+  test_exchange_api_keys_cherry_picking_new \
+  test_exchange_api_new \
   test_exchange_api \
-  test_exchange_api_keys_cherry_picking \
-  test_exchange_api_new
+  test_exchange_api_keys_cherry_picking
 
 AM_TESTS_ENVIRONMENT=export 
TALER_PREFIX=$${TALER_PREFIX:address@hidden@};export 
PATH=$${TALER_PREFIX:address@hidden@}/bin:$$PATH;
 
+# FIXME: uncomment those.
 TESTS = \
+  test_exchange_api_keys_cherry_picking_new \
+  test_exchange_api_new \
   test_exchange_api \
   test_exchange_api_keys_cherry_picking
 
@@ -108,6 +129,19 @@ test_exchange_api_new_LDADD = \
   -lgnunetutil \
   -ljansson
 
+test_exchange_api_keys_cherry_picking_new_SOURCES = \
+  test_exchange_api_keys_cherry_picking_new.c
+test_exchange_api_keys_cherry_picking_new_LDADD = \
+  libtalertesting.la \
+  libtalerexchange.la \
+  $(LIBGCRYPT_LIBS) \
+  $(top_builddir)/src/json/libtalerjson.la \
+  $(top_builddir)/src/util/libtalerutil.la \
+  $(top_builddir)/src/bank-lib/libtalerbank.la \
+  -lgnunetcurl \
+  -lgnunetutil \
+  -ljansson
+
 test_exchange_api_keys_cherry_picking_SOURCES = \
   test_exchange_api_keys_cherry_picking.c
 test_exchange_api_keys_cherry_picking_LDADD = \
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 2b94dba..b2d2fbc 100644
--- a/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf
+++ b/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf
@@ -1,7 +1,7 @@
 # This file is in the public domain.
 #
 [PATHS]
-# Persistant data storage for the testcase
+# Persistent data storage for the testcase
 TALER_TEST_HOME = test_exchange_api_home/
 
 [taler]
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
new file mode 100644
index 0000000..c32d642
--- /dev/null
+++ b/src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c
@@ -0,0 +1,135 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/test_exchange_api_keys_cherry_picking_new.c
+ * @brief testcase to test exchange's /keys cherry picking ability
+ * @author Marcello Stanisci
+ * @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"
+#include "taler_testing_lib.h"
+
+/**
+ * Configuration file we use.  One (big) configuration is used
+ * for the various components for this test.
+ */
+#define CONFIG_FILE "test_exchange_api_keys_cherry_picking.conf"
+
+/**
+ * Used to increase the number of denomination keys.
+ */
+#define CONFIG_FILE_EXTENDED \
+  "test_exchange_api_keys_cherry_picking_extended.conf"
+
+
+/**
+ * Main function that will tell the interpreter what commands to
+ * run.
+ *
+ * @param cls closure
+ */
+static void
+run (void *cls,
+     struct TALER_TESTING_Interpreter *is)
+{
+  struct TALER_TESTING_Command commands[] = {
+
+    /* Send signal to the exchange to see if it reacts */
+    TALER_TESTING_cmd_signal ("signal-reaction-1",
+                              is->exchanged,
+                              SIGUSR1),
+
+    TALER_TESTING_cmd_check_keys ("check-keys-1",
+                                  1, 4,
+                                  is->exchange),
+
+    TALER_TESTING_cmd_exec_keyup ("keyup-2", /* 1st keyup happens at start-up 
*/
+                                  CONFIG_FILE_EXTENDED),
+
+    TALER_TESTING_cmd_exec_auditor_sign ("sign-keys-1",
+                                         CONFIG_FILE),
+
+    TALER_TESTING_cmd_signal ("trigger-keys-reload-1",
+                              is->exchanged,
+                              SIGUSR1),
+
+    TALER_TESTING_cmd_check_keys ("check-keys-2",
+                                  2, 8,
+                                  is->exchange),
+
+
+    /**
+     * End the suite.  Fixme: better to have a label for this
+     * too, as it shows "(null)" in logs.
+     */
+    TALER_TESTING_cmd_end ()
+  };
+
+  TALER_TESTING_run (is, commands);
+}
+
+int
+main (int argc,
+      char * const *argv)
+{
+  /* These environment variables get in the way... */
+  unsetenv ("XDG_DATA_HOME");
+  unsetenv ("XDG_CONFIG_HOME");
+  GNUNET_log_setup ("test-exchange-api-cherry-picking-new",
+                    "DEBUG", NULL);
+  TALER_TESTING_cleanup_files (CONFIG_FILE);
+  /* @helpers.  Run keyup, create tables, ... Note: it
+   * fetches the port number from config in order to see
+   * if it's available. */
+  switch (TALER_TESTING_prepare_exchange (CONFIG_FILE))
+  {
+  case GNUNET_SYSERR:
+    GNUNET_break (0);
+    return 1;
+  case GNUNET_NO:
+    return 77;
+  case GNUNET_OK:
+    if (GNUNET_OK !=
+        /* Set up event loop and reschedule context, plus
+         * start/stop the exchange.  It calls TALER_TESTING_setup
+         * which creates the 'is' object.
+         */
+        TALER_TESTING_setup_with_exchange (&run,
+                                           NULL,
+                                           CONFIG_FILE))
+      return 1;
+    break;
+  default:
+    GNUNET_break (0);
+    return 1;
+  }
+  return 0;
+}
+
+/* end of test_exchange_api_keys_cherry_picking_new.c */
diff --git a/src/exchange-lib/test_exchange_api_new.c 
b/src/exchange-lib/test_exchange_api_new.c
index f9d6024..580c4e9 100644
--- a/src/exchange-lib/test_exchange_api_new.c
+++ b/src/exchange-lib/test_exchange_api_new.c
@@ -2,23 +2,29 @@
   This file is part of TALER
   Copyright (C) 2014-2018 Taler Systems SA
 
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
+  TALER is 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.
+  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/>
+  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_new.c
  * @brief testcase to test exchange's HTTP API interface
  * @author Sree Harsha Totakura <address@hidden>
  * @author Christian Grothoff
+ * @author Marcello Stanisci
  */
+
 #include "platform.h"
 #include "taler_util.h"
 #include "taler_signatures.h"
@@ -37,10 +43,17 @@
 #define CONFIG_FILE "test_exchange_api.conf"
 
 /**
- * URL of the fakebank.  Obtained from CONFIG_FILE's 
"exchange-wire-test:BANK_URL" option.
+ * URL of the fakebank.  Obtained from CONFIG_FILE's
+ * "exchange-wire-test:BANK_URI" option.
  */
 static char *fakebank_url;
 
+/**
+ * FIXME: what about putting exchange_url also global
+ * here?  Right now, the exchange port is being "bounced"
+ * between functions before exchange_url is constructed
+ * and TALER_EXCHANGE_connect() is called with that.
+ */
 
 /**
  * Account number of the exchange at the bank.
@@ -72,6 +85,15 @@ static char *fakebank_url;
    TALER_TESTING_cmd_exec_wirewatch (label, CONFIG_FILE)
 
 /**
+ * Execute the taler-exchange-aggregator command with
+ * our configuration file.
+ *
+ * @param label label to use for the command.
+ */
+#define CMD_EXEC_AGGREGATOR(label) \
+   TALER_TESTING_cmd_exec_aggregator (label, CONFIG_FILE)
+
+/**
  * Run wire transfer of funds from some user's account to the
  * exchange.
  *
@@ -83,9 +105,22 @@ static char *fakebank_url;
      fakebank_url, USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO, \
      USER_LOGIN_NAME, USER_LOGIN_PASS)
 
+/**
+ * Run wire transfer of funds from some user's account to the
+ * exchange.
+ *
+ * @param label label to use for the command.
+ * @param amount amount to transfer, i.e. "EUR:1"
+ */
+#define CMD_TRANSFER_TO_EXCHANGE_SUBJECT(label,amount,subject) \
+   TALER_TESTING_cmd_fakebank_transfer_with_subject \
+     (label, amount, fakebank_url, USER_ACCOUNT_NO, \
+      EXCHANGE_ACCOUNT_NO, USER_LOGIN_NAME, USER_LOGIN_PASS, \
+      subject)
 
 /**
- * Main function that will tell the interpreter what commands to run.
+ * Main function that will tell the interpreter what commands to
+ * run.
  *
  * @param cls closure
  */
@@ -94,9 +129,609 @@ run (void *cls,
      struct TALER_TESTING_Interpreter *is)
 {
   struct TALER_TESTING_Command commands[] = {
+
+    /****** Start of "wire" testing ******/
+
+    /**
+     * Move money to the exchange's bank account.
+     */
     CMD_TRANSFER_TO_EXCHANGE ("create-reserve-1",
                               "EUR:5.01"),
-    CMD_EXEC_WIREWATCH ("exec-wirewatch-1"),
+
+    /**
+     * Make a reserve exist, according to the previous
+     * transfer.
+     */
+    CMD_EXEC_WIREWATCH ("wirewatch-1"),
+
+    /**
+     * Check if 'test' wire method is offered by the exchange.
+     */
+    TALER_TESTING_cmd_wire ("wire-test-1",
+                            is->exchange,
+                            "test",
+                            NULL,
+                            MHD_HTTP_OK),
+
+    /**
+     * Check if 'sepa' wire method is offered by the exchange.
+     */
+    TALER_TESTING_cmd_wire ("wire-sepa-1",
+                            is->exchange,
+                            "sepa",
+                            NULL,
+                            MHD_HTTP_OK),
+
+    /****** End of "wire" testing ******/
+
+    /******  Start of withdraw and spend testing ******/
+
+    /**
+     * Withdraw EUR:5.
+     */
+    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1",
+                                       is->exchange,
+                                       "create-reserve-1",
+                                       "EUR:5",
+                                       MHD_HTTP_OK),
+
+    /**
+     * Check the reserve is depleted.
+     */
+    TALER_TESTING_cmd_status ("status-1",
+                              is->exchange,
+                              "create-reserve-1",
+                              "EUR:0",
+                              MHD_HTTP_OK),
+    /**
+     * Spend the coin.
+     */
+    TALER_TESTING_cmd_deposit
+      ("deposit-simple", is->exchange, "withdraw-coin-1", 0,
+       TALER_TESTING_make_wire_details
+         ("{ \"type\":\"test\",\"account_number\":42}",
+          fakebank_url),
+       "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
+       GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_OK),
+
+    /**
+     * Try to overdraw.
+     */
+    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2",
+                                       is->exchange,
+                                       "create-reserve-1",
+                                       "EUR:5",
+                                       MHD_HTTP_FORBIDDEN),
+
+    /**
+     * Try to double spend using different wire details.
+     */
+    TALER_TESTING_cmd_deposit
+      ("deposit-double-1", is->exchange, "withdraw-coin-1", 0,
+       TALER_TESTING_make_wire_details
+         ("{\"type\":\"test\",\"account_number\":43}",
+          fakebank_url),
+       "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
+       GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN),
+
+    /**
+     * Try to double spend using a different transaction id.
+     * (copied verbatim from old exchange-lib tests.)
+     * FIXME: how can it get a different transaction id?  There
+     * isn't such a thing actually, the exchange only knows about
+     * contract terms' hashes.  So since the contract terms are
+     * exactly the same as the previous command, how can a different
+     * id be generated?
+     */
+    TALER_TESTING_cmd_deposit
+      ("deposit-double-1", is->exchange, "withdraw-coin-1", 0,
+       TALER_TESTING_make_wire_details
+         ("{ \"type\":\"test\", \"account_number\":43}",
+          fakebank_url),
+       "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
+       GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN),
+
+    /**
+     * Try to double spend with different proposal.
+     */
+    TALER_TESTING_cmd_deposit
+      ("deposit-double-2", is->exchange, "withdraw-coin-1", 0,
+       TALER_TESTING_make_wire_details
+         ("{ \"type\":\"test\", \"account_number\":43}",
+          fakebank_url),
+       "{\"items\":[{\"name\":\"ice cream\",\"value\":2}]}",
+       GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN),
+    
+    /******  End of withdraw and spend testing ******/
+
+    /******  Start of refresh testing ******/
+
+    /**
+     * Fill reserve with EUR:5, 1ct is for fees.  NOTE: the old
+     * test-suite gave a account number of _424_ to the user at
+     * this step; to type less, here the _42_ number is reused.
+     * Does this change the tests semantics?
+     */
+    CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve-1",
+                              "EUR:5.01"),
+ 
+    /**
+     * Make previous command effective.
+     */
+    CMD_EXEC_WIREWATCH ("wirewatch-2"),
+
+    /**
+     * Withdraw EUR:5.
+     */
+    TALER_TESTING_cmd_withdraw_amount
+      ("refresh-withdraw-coin-1",
+       is->exchange,
+       "refresh-create-reserve-1",
+       "EUR:5",
+       MHD_HTTP_OK),
+    /**
+     * 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)
+     */
+    TALER_TESTING_cmd_deposit
+      ("refresh-deposit-partial", is->exchange,
+       "refresh-withdraw-coin-1", 0,
+       TALER_TESTING_make_wire_details
+         ("{ \"type\":\"test\",\"account_number\":42}",
+          fakebank_url),
+       "{\"items\":[{\"name\":\"ice cream\",\
+                     \"value\":\"EUR:1\"}]}",
+       GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_OK),
+
+    /**
+     * Melt the rest of the coin's value
+     * (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
+    TALER_TESTING_cmd_refresh_melt
+      ("refresh-melt-1", is->exchange, "EUR:4",
+       "refresh-withdraw-coin-1", MHD_HTTP_OK),
+    /**
+     * Complete (successful) melt operation, and
+     * withdraw the coins
+     */
+    TALER_TESTING_cmd_refresh_reveal
+      ("refresh-reveal-1", is->exchange,
+       "refresh-melt-1", MHD_HTTP_OK),
+    
+    /**
+     * Do it again to check idempotency
+     */
+    TALER_TESTING_cmd_refresh_reveal
+      ("refresh-reveal-1-idempotency",
+       is->exchange, "refresh-melt-1", MHD_HTTP_OK),
+
+    /**
+     * Test that /refresh/link works
+     */
+    TALER_TESTING_cmd_refresh_link
+      ("refresh-link-1", is->exchange,
+       "refresh-reveal-1", MHD_HTTP_OK),
+
+    /**
+     * Try to spend a refreshed EUR:1 coin
+     */
+    TALER_TESTING_cmd_deposit
+      ("refresh-deposit-refreshed-1a", is->exchange,
+       "refresh-reveal-1-idempotency", 0,
+       TALER_TESTING_make_wire_details
+         ("{ \"type\":\"test\",\"account_number\":42}",
+          fakebank_url),
+       "{\"items\":[{\"name\":\"ice cream\",\
+                     \"value\":3}]}",
+       GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_OK),
+
+    /**
+     * Try to spend a refreshed EUR:0.1 coin
+     */
+    TALER_TESTING_cmd_deposit
+      ("refresh-deposit-refreshed-1b", is->exchange,
+       "refresh-reveal-1", 4,
+       TALER_TESTING_make_wire_details
+         ("{ \"type\":\"test\",\"account_number\":43}",
+          fakebank_url),
+       "{\"items\":[{\"name\":\"ice cream\",\
+                     \"value\":3}]}",
+       GNUNET_TIME_UNIT_ZERO, "EUR:0.1", MHD_HTTP_OK),
+
+    /* Test running a failing melt operation (same operation
+     * again must fail) */
+    TALER_TESTING_cmd_refresh_melt
+      ("refresh-melt-failing", is->exchange, "EUR:4",
+       "refresh-withdraw-coin-1", MHD_HTTP_FORBIDDEN),
+
+    /* 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.
+     */
+    TALER_TESTING_cmd_track_transaction
+    ("deposit-wtid-found", is->exchange,
+     "deposit-simple", 0, MHD_HTTP_ACCEPTED, NULL),
+
+    /**
+     * 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.
+     */
+    TALER_TESTING_cmd_track_transaction
+    ("deposit-wtid-failing", is->exchange,
+     "deposit-double-2", 0, MHD_HTTP_NOT_FOUND, NULL),
+
+    /**
+     * Try resolving an undefined (all zeros) WTID; this
+     * should fail as obviously the exchange didn't use that
+     * WTID value for any transaction.
+     */
+    TALER_TESTING_cmd_track_transfer_empty
+      ("wire-deposit-failing", is->exchange,
+       NULL, 0, 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! NOTE: this comment comes
+     * "verbatim" from the old test-suite, and IMO does not explain
+     * a lot!
+     */
+    CMD_EXEC_AGGREGATOR ("run-aggregator"),
+
+    /**
+     * Check all the transfers took place.
+     */
+    TALER_TESTING_cmd_check_bank_transfer
+      ("check_bank_transfer-499c", "https://exchange.com/";,
+       "EUR:4.98", 2, 42),
+
+    TALER_TESTING_cmd_check_bank_transfer
+      ("check_bank_transfer-99c1", "https://exchange.com/";,
+       "EUR:0.98", 2, 42),
+
+    TALER_TESTING_cmd_check_bank_transfer
+      ("check_bank_transfer-99c2", "https://exchange.com/";,
+       "EUR:0.98", 2, 42),
+
+    TALER_TESTING_cmd_check_bank_transfer
+      ("check_bank_transfer-99c", "https://exchange.com/";,
+       "EUR:0.08", 2, 43),
+
+    TALER_TESTING_cmd_check_bank_transfer
+      ("check_bank_transfer-aai-1", "https://exchange.com/";,
+       "EUR:5.01", 42, 2),
+
+    /**
+     * NOTE: the old test-suite had this "check bank transfer"
+     * command with debit account == 424.
+     */
+    TALER_TESTING_cmd_check_bank_transfer
+      ("check_bank_transfer-aai-2", "https://exchange.com/";,
+       "EUR:5.01", 42, 2),
+
+    TALER_TESTING_cmd_check_bank_empty ("check_bank_empty"),
+
+    TALER_TESTING_cmd_track_transaction
+    ("deposit-wtid-ok", is->exchange,
+     "deposit-simple", 0, MHD_HTTP_OK, "check_bank_transfer-499c"),
+
+    TALER_TESTING_cmd_track_transfer
+      ("wire-deposit-success-bank", is->exchange,
+       "check_bank_transfer-99c1", 0, MHD_HTTP_OK, "EUR:0.98",
+       "EUR:0.01"),
+
+    TALER_TESTING_cmd_track_transfer
+      ("wire-deposits-success-wtid", is->exchange,
+       "deposit-wtid-ok", 0, MHD_HTTP_OK, "EUR:4.98",
+       "EUR:0.01"),
+
+    /* **** End of test tracking API ***** */
+
+    /* **** test /refund API ***** */
+
+    /**
+     * Fill reserve with EUR:5.01, as withdraw fee is 1 ct per
+     * config.
+     */
+    CMD_TRANSFER_TO_EXCHANGE ("create-reserve-r1",
+                              "EUR:5.01"),
+
+
+    /**
+     * Run wire-watch to trigger the reserve creation.
+     */
+    CMD_EXEC_WIREWATCH ("wirewatch-3"),
+
+    /* Withdraw a 5 EUR coin, at fee of 1 ct */
+    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r1",
+                                       is->exchange,
+                                       "create-reserve-r1",
+                                       "EUR:5",
+                                       MHD_HTTP_OK),
+    /**
+     * Spend 5 EUR of the 5 EUR coin (in full) (merchant would
+     * receive EUR:4.99 due to 1 ct deposit fee)
+     */
+    TALER_TESTING_cmd_deposit
+      ("deposit-refund-1", is->exchange, "withdraw-coin-r1", 0,
+       TALER_TESTING_make_wire_details
+         ("{ \"type\":\"test\", \"account_number\":42}",
+          fakebank_url),
+       "{\"items\":[{\"name\":\"ice cream\","
+                    "\"value\":\"EUR:5\"}]}",
+       GNUNET_TIME_UNIT_MINUTES, "EUR:5", MHD_HTTP_OK),
+
+
+    /**
+     * Run transfers. Should do nothing as refund deadline blocks
+     * it
+     */
+    CMD_EXEC_AGGREGATOR ("run-aggregator-refund"),
+
+    /**
+     * Check that aggregator didn't do anything, as expected.
+     * Note, this operation takes two commands: one to "flush"
+     * the preliminary transfer (used to withdraw) from the
+     * fakebank and the second to actually check there are not
+     * other transfers around.
+     */
+
+    TALER_TESTING_cmd_check_bank_transfer
+      ("check_bank_transfer-pre-refund", "https://exchange.com/";,
+       "EUR:5.01", 42, 2),
+
+    TALER_TESTING_cmd_check_bank_empty
+      ("check_bank_transfer-pre-refund"),
+
+    TALER_TESTING_cmd_refund
+      ("refund-ok", MHD_HTTP_OK,
+       "EUR:5", "EUR:0.01", "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) */
+    TALER_TESTING_cmd_deposit
+      ("deposit-refund-2", is->exchange, "withdraw-coin-r1", 0,
+       TALER_TESTING_make_wire_details
+         ("{ \"type\":\"test\", \"account_number\":42}",
+          fakebank_url),
+       "{\"items\":[{\"name\":\"more ice cream\","
+                    "\"value\":\"EUR:5\"}]}",
+       GNUNET_TIME_UNIT_ZERO, "EUR:4.99", MHD_HTTP_OK),
+
+
+    /**
+     * Run transfers. This will do the transfer as refund deadline
+     * was 0
+     */
+    CMD_EXEC_AGGREGATOR ("run-aggregator-3"),
+
+    /**
+     * Check that deposit did run.
+     */
+    TALER_TESTING_cmd_check_bank_transfer
+      ("check_bank_transfer-pre-refund", "https://exchange.com/";,
+       "EUR:4.97", 2, 42),
+
+    /**
+     * Run failing refund, as past deadline & aggregation.
+     */
+    TALER_TESTING_cmd_refund
+      ("refund-fail", MHD_HTTP_GONE,
+       "EUR:4.99", "EUR:0.01", "deposit-refund-2"),
+
+    TALER_TESTING_cmd_check_bank_empty
+      ("check-empty-after-refund"),
+
+    /**
+     * Test refunded coins are never executed, even past
+     * refund deadline
+     */
+    CMD_TRANSFER_TO_EXCHANGE ("create-reserve-rb",
+                              "EUR:5.01"),
+
+    CMD_EXEC_WIREWATCH ("wirewatch-rb"),
+
+    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-rb",
+                                       is->exchange,
+                                       "create-reserve-rb",
+                                       "EUR:5",
+                                       MHD_HTTP_OK),
+
+    TALER_TESTING_cmd_check_bank_transfer
+      ("check_bank_transfer-aai-3b", "https://exchange.com/";,
+       "EUR:5.01", 42, 2),
+
+
+    TALER_TESTING_cmd_deposit
+      ("deposit-refund-1b", is->exchange, "withdraw-coin-rb", 0,
+       TALER_TESTING_make_wire_details
+         ("{ \"type\":\"test\", \"account_number\":42}",
+          fakebank_url),
+       "{\"items\":[{\"name\":\"ice cream\","
+                    "\"value\":\"EUR:5\"}]}",
+       GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_OK),
+
+    /**
+     * Trigger refund (before aggregator had a chance to execute
+     * deposit, even though refund deadline was zero).
+     */
+    TALER_TESTING_cmd_refund
+      ("refund-ok-fast", MHD_HTTP_OK,
+       "EUR:5", "EUR:0.01", "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.
+     */
+    CMD_EXEC_AGGREGATOR ("run-aggregator-3b"),
+
+    /* check that aggregator didn't do anything, as expected */
+    TALER_TESTING_cmd_check_bank_empty
+      ("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.
+     */
+    CMD_TRANSFER_TO_EXCHANGE ("payback-create-reserve-1",
+                              "EUR:5.01"),
+
+    /**
+     * Run wire-watch to trigger the reserve creation.
+     */
+    CMD_EXEC_WIREWATCH ("wirewatch-4"),
+
+    /* Withdraw a 5 EUR coin, at fee of 1 ct */
+    TALER_TESTING_cmd_withdraw_amount ("payback-withdraw-coin-1",
+                                       is->exchange,
+                                       "payback-create-reserve-1",
+                                       "EUR:5",
+                                       MHD_HTTP_OK), 
+
+    TALER_TESTING_cmd_revoke ("revoke-1", MHD_HTTP_OK,
+                              "payback-withdraw-coin-1",
+                              CONFIG_FILE),
+
+    TALER_TESTING_cmd_payback ("payback-1", MHD_HTTP_OK,
+                               "payback-withdraw-coin-1", "EUR:5"),
+
+    /* Check the money is back with the reserve */
+    TALER_TESTING_cmd_status ("payback-reserve-status-1",
+                              is->exchange,
+                              "payback-create-reserve-1",
+                              "EUR:5.0",
+                              MHD_HTTP_OK),
+
+    /**
+     * 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...)
+     */
+    CMD_TRANSFER_TO_EXCHANGE ("payback-create-reserve-2",
+                              "EUR:2.02"),
+
+    /* Make previous command effective. */
+    CMD_EXEC_WIREWATCH ("wirewatch-5"),
+
+    /* Withdraw a 1 EUR coin, at fee of 1 ct */
+    TALER_TESTING_cmd_withdraw_amount ("payback-withdraw-coin-2a",
+                                       is->exchange,
+                                       "payback-create-reserve-2",
+                                       "EUR:1",
+                                       MHD_HTTP_OK),
+
+    /* Withdraw a 1 EUR coin, at fee of 1 ct */
+    TALER_TESTING_cmd_withdraw_amount ("payback-withdraw-coin-2b",
+                                       is->exchange,
+                                       "payback-create-reserve-2",
+                                       "EUR:1",
+                                       MHD_HTTP_OK),
+
+    TALER_TESTING_cmd_deposit
+      ("payback-deposit-partial", is->exchange,
+       "payback-withdraw-coin-2a", 0,
+       TALER_TESTING_make_wire_details
+         ("{ \"type\":\"test\",\"account_number\":42}",
+          fakebank_url),
+       "{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}",
+       GNUNET_TIME_UNIT_ZERO, "EUR:0.5", MHD_HTTP_OK),
+
+
+    TALER_TESTING_cmd_revoke ("revoke-2", MHD_HTTP_OK,
+                              "payback-withdraw-coin-2a",
+                              CONFIG_FILE),
+
+    TALER_TESTING_cmd_payback ("payback-2", MHD_HTTP_OK,
+                               "payback-withdraw-coin-2a",
+                               "EUR:0.5"),
+
+    TALER_TESTING_cmd_payback ("payback-2b", MHD_HTTP_FORBIDDEN,
+                               "payback-withdraw-coin-2a",
+                               "EUR:0.5"),
+
+    TALER_TESTING_cmd_deposit
+      ("payback-deposit-revoked", is->exchange,
+       "payback-withdraw-coin-2b", 0,
+       TALER_TESTING_make_wire_details
+         ("{ \"type\":\"test\",\"account_number\":42}",
+          fakebank_url),
+       "{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}",
+       GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_NOT_FOUND),
+
+
+    /* 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! */
+    TALER_TESTING_cmd_deposit
+      ("payback-deposit-partial-after-payback", is->exchange,
+       "payback-withdraw-coin-2a", 0,
+       TALER_TESTING_make_wire_details
+         ("{ \"type\":\"test\",\"account_number\":42}",
+          fakebank_url),
+       "{\"items\":[{\"name\":\"extra ice cream\",\"value\":1}]}",
+       GNUNET_TIME_UNIT_ZERO, "EUR:0.5", MHD_HTTP_NOT_FOUND),
+
+    /* Test that revoked coins cannot be withdrawn */
+    CMD_TRANSFER_TO_EXCHANGE ("payback-create-reserve-3",
+                              "EUR:1.01"),
+
+    CMD_EXEC_WIREWATCH ("wirewatch-6"),
+
+    TALER_TESTING_cmd_withdraw_amount
+      ("payback-withdraw-coin-3-revoked",
+       is->exchange,
+       "payback-create-reserve-3",
+       "EUR:1",
+       MHD_HTTP_NOT_FOUND),
+
+    /* check that we are empty before the rejection test */
+    TALER_TESTING_cmd_check_bank_transfer
+      ("check_bank_transfer-pr1", "https://exchange.com/";,
+       "EUR:5.01", 42, 2),
+    TALER_TESTING_cmd_check_bank_transfer
+      ("check_bank_transfer-pr2", "https://exchange.com/";,
+       "EUR:2.02", 42, 2),
+    TALER_TESTING_cmd_check_bank_transfer
+      ("check_bank_transfer-pr3", "https://exchange.com/";,
+       "EUR:1.01", 42, 2),
+
+    TALER_TESTING_cmd_check_bank_empty ("check-empty-again"),
+
+    /* Test rejection of bogus wire transfers */
+    CMD_TRANSFER_TO_EXCHANGE_SUBJECT ("bogus-subject",
+                                      "EUR:1.01",
+                                      "not a reserve public key"),
+
+    CMD_EXEC_WIREWATCH ("wirewatch-7"),
+
+    TALER_TESTING_cmd_check_bank_empty ("check-empty-from-reject"),
+
+    /* ************** End of /payback API  ************* */
+
+    /**
+     * End the suite.  Fixme: better to have a label for this
+     * too, as it shows a "(null)" token on logs.
+     */
     TALER_TESTING_cmd_end ()
   };
 
@@ -105,7 +740,6 @@ run (void *cls,
                                    fakebank_url);
 }
 
-
 int
 main (int argc,
       char * const *argv)
@@ -116,9 +750,15 @@ main (int argc,
   GNUNET_log_setup ("test-exchange-api-new",
                     "INFO",
                     NULL);
-  if (NULL == (fakebank_url = TALER_TESTING_prepare_fakebank (CONFIG_FILE)))
+  if (NULL == (fakebank_url
+       /* Check fakebank port is available and config cares
+        * about bank url. */
+       = TALER_TESTING_prepare_fakebank (CONFIG_FILE)))
     return 77;
   TALER_TESTING_cleanup_files (CONFIG_FILE);
+  /* @helpers.  Run keyup, create tables, ... Note: it
+   * fetches the port number from config in order to see
+   * if it's available. */
   switch (TALER_TESTING_prepare_exchange (CONFIG_FILE))
   {
   case GNUNET_SYSERR:
@@ -128,10 +768,15 @@ main (int argc,
     return 77;
   case GNUNET_OK:
     if (GNUNET_OK !=
+        /* Set up event loop and reschedule context, plus
+         * start/stop the exchange.  It calls TALER_TESTING_setup
+         * which creates the 'is' object.
+         */
         TALER_TESTING_setup_with_exchange (&run,
                                            NULL,
                                            CONFIG_FILE))
       return 1;
+    break;
   default:
     GNUNET_break (0);
     return 1;
diff --git a/src/exchange-lib/testing_api_cmd_bank_check.c 
b/src/exchange-lib/testing_api_cmd_bank_check.c
new file mode 100644
index 0000000..ab55224
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_bank_check.c
@@ -0,0 +1,268 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 3, or
+  (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_bank_check.c
+ * @brief command to check if a particular wire transfer took
+ *        place.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+#include "taler_fakebank_lib.h"
+
+struct BankCheckState
+{
+
+  /**
+   * Exchange base URL (Fixme: why?)
+   */
+  const char *exchange_base_url;
+
+  /**
+   * Expected transferred amount.
+   */
+  const char *amount;
+
+  /**
+   * Expected account number that gave money
+   */
+  unsigned int debit_account;
+ 
+  /**
+   * Expected account number that received money
+   */
+  unsigned int credit_account;
+
+  /**
+   * Wire transfer subject (set by fakebank-lib).
+   */
+  char *subject;
+
+  /**
+   * Binary form of the transfer subject.  Some commands expect
+   * it - via appropriate traits - to be in binary form.
+   */
+  struct TALER_WireTransferIdentifierRawP wtid;
+
+  /**
+   * Interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+};
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param is the interpreter state.
+ */
+void
+check_bank_transfer_run (void *cls,
+                         const struct TALER_TESTING_Command *cmd,
+                         struct TALER_TESTING_Interpreter *is)
+{
+  struct BankCheckState *bcs = cls;
+  struct TALER_Amount amount;
+
+
+  if (GNUNET_OK !=
+      TALER_string_to_amount (bcs->amount,
+                              &amount))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to parse amount `%s' at %u\n",
+                bcs->amount,
+                is->ip);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  if (GNUNET_OK !=
+      TALER_FAKEBANK_check (is->fakebank,
+                            &amount,
+                            bcs->debit_account,
+                            bcs->credit_account,
+                            bcs->exchange_base_url,
+                            &bcs->subject))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  TALER_TESTING_interpreter_next (is);
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+check_bank_transfer_cleanup
+  (void *cls,
+   const struct TALER_TESTING_Command *cmd)
+{
+  struct BankCheckState *bcs = cls;
+ 
+  GNUNET_free_non_null (bcs->subject);
+  GNUNET_free (bcs);
+}
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+check_bank_transfer_traits (void *cls,
+                            void **ret,
+                            const char *trait,
+                            unsigned int index)
+{
+
+
+  struct BankCheckState *bcs = cls; 
+
+  GNUNET_assert (GNUNET_OK == 
+    GNUNET_STRINGS_string_to_data
+      (bcs->subject,
+       strlen (bcs->subject),
+       &bcs->wtid,
+       sizeof (struct TALER_WireTransferIdentifierRawP)));
+
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_transfer_subject (0, bcs->subject),
+    TALER_TESTING_make_trait_wtid (0, &bcs->wtid),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}                           
+
+
+
+/**
+ * Command to check whether a particular wire transfer has been
+ * made or not.
+ *
+ * @param label the command label
+ * @param exchange_base_url base url of the exchange (Fixme: why?)
+ * @param amount the amount expected to be transferred
+ * @param debit_account the account that gave money
+ * @param credit_account the account that received money
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_check_bank_transfer
+  (const char *label,
+   const char *exchange_base_url,
+   const char *amount,
+   unsigned int debit_account,
+   unsigned int credit_account)
+{
+  struct BankCheckState *bcs;
+  struct TALER_TESTING_Command cmd;
+
+  bcs = GNUNET_new (struct BankCheckState);
+  bcs->exchange_base_url = exchange_base_url;
+  bcs->amount = amount;
+  bcs->debit_account = debit_account;
+  bcs->credit_account = credit_account;
+
+  cmd.label = label;
+  cmd.cls = bcs;
+  cmd.run = &check_bank_transfer_run;
+  cmd.cleanup = &check_bank_transfer_cleanup;
+  // traits?
+  cmd.traits = &check_bank_transfer_traits;
+
+  return cmd;
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+check_bank_empty_cleanup
+  (void *cls,
+   const struct TALER_TESTING_Command *cmd)
+{
+  return;
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param is the interpreter state.
+ */
+void
+check_bank_empty_run (void *cls,
+                      const struct TALER_TESTING_Command *cmd,
+                      struct TALER_TESTING_Interpreter *is)
+{
+
+  if (GNUNET_OK != TALER_FAKEBANK_check_empty (is->fakebank))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;  
+  }
+  TALER_TESTING_interpreter_next (is);
+}
+
+/**
+ * Check bank's balance is zero.
+ *
+ * @param credit_account the account that received money
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_check_bank_empty (const char *label)
+{
+  struct TALER_TESTING_Command cmd;
+
+  cmd.label = label;
+  cmd.run = &check_bank_empty_run;
+  cmd.cleanup = &check_bank_empty_cleanup;
+  
+  return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_check_keys.c 
b/src/exchange-lib/testing_api_cmd_check_keys.c
new file mode 100644
index 0000000..8f77a83
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_check_keys.c
@@ -0,0 +1,153 @@
+/*
+  This file is part of TALER
+  (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 3, or
+  (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_check_keys.c
+ * @brief Implementation of "check keys" test command.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct CheckKeysState
+{
+  /**
+   * FIXME
+   */
+  unsigned int generation;
+
+  /**
+   * FIXME
+   */
+  unsigned int num_denom_keys;
+
+  /**
+   * Connection to the exchange.
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+};
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct SignalState.
+ * @param cmd the command to execute, a /wire one.
+ * @param is the interpreter state.
+ */
+void
+check_keys_run (void *cls,
+                const struct TALER_TESTING_Command *cmd,
+                struct TALER_TESTING_Interpreter *is)
+{
+  struct CheckKeysState *cks = cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "cmd `%s', key generation: %d\n",
+              cmd->label, is->key_generation);
+
+  if (is->key_generation < cks->generation)
+  {
+    /* Go back to waiting for /keys signal! */
+    is->working = GNUNET_NO;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Triggering /keys dl, cmd `%s'\n",
+                cmd->label);
+    GNUNET_break (0 == TALER_EXCHANGE_check_keys_current
+      (cks->exchange, GNUNET_YES).abs_value_us);
+    return;
+  }
+  if (is->key_generation > cks->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);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  /* /keys was updated, let's check they were OK! */
+  if (cks->num_denom_keys != is->keys->num_denom_keys)
+  {
+    /* Did not get the expected number of denomination keys! */
+    GNUNET_break (0);
+    fprintf (stderr, "Got %u keys in step %s\n",
+             is->keys->num_denom_keys, cmd->label);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  TALER_TESTING_interpreter_next (is);
+}
+
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct SignalState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+check_keys_cleanup (void *cls,
+                    const struct TALER_TESTING_Command *cmd)
+{
+  struct CheckKeysState *cks = cls;
+
+  GNUNET_free (cks);
+}
+
+/**
+ * Make a "check keys" command.
+ *
+ * @param label command label
+ * @param generation FIXME
+ * @param num_denom_keys FIXME
+ *
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_check_keys
+  (const char *label,
+   unsigned int generation,
+   unsigned int num_denom_keys,
+   struct TALER_EXCHANGE_Handle *exchange)
+{
+
+  struct CheckKeysState *cks;
+  struct TALER_TESTING_Command cmd;
+
+  cks = GNUNET_new (struct CheckKeysState);
+
+  cks->generation = generation;
+  cks->num_denom_keys = num_denom_keys;
+  cks->exchange = exchange;
+
+  cmd.cls = cks;
+  cmd.label = label;
+  cmd.run = &check_keys_run;
+  cmd.cleanup = &check_keys_cleanup;
+
+  return cmd;
+
+
+}
diff --git a/src/exchange-lib/testing_api_cmd_deposit.c 
b/src/exchange-lib/testing_api_cmd_deposit.c
new file mode 100644
index 0000000..f795159
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_deposit.c
@@ -0,0 +1,448 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_deposit.c
+ * @brief command for testing /deposit.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+#include "taler_signatures.h"
+
+struct DepositState
+{
+
+  /**
+   * Amount to deposit.
+   */
+  const char *amount;
+
+  /**
+   * Reference to any command that is able to provide a coin.
+   */
+  const char *coin_reference;
+
+  /**
+   * If this @e coin_ref refers to an operation that generated
+   * an array of coins, this value determines which coin to pick.
+   */
+  unsigned int coin_index;
+
+  /**
+   * JSON string describing the merchant's "wire details".
+   */
+  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.
+   */
+  struct TALER_MerchantPrivateKeyP merchant_priv;
+
+  /**
+   * Deposit handle while operation is running.
+   */
+  struct TALER_EXCHANGE_DepositHandle *dh;
+
+  /**
+   * Expected HTTP response code.
+   */
+  unsigned int expected_response_code;
+
+  /**
+   * Interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+
+  /**
+   * Exchange connection.
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+};
+
+/**
+ * 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 DepositState *ds = cls;
+
+  ds->dh = NULL;
+  if (ds->expected_response_code != http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u to command %s\n",
+                http_status,
+                ds->is->commands[ds->is->ip].label);
+    json_dumpf (obj, stderr, 0);
+    TALER_TESTING_interpreter_fail (ds->is);
+    return;
+  }
+  TALER_TESTING_interpreter_next (ds->is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param i the interpreter state.
+ */
+void
+deposit_run (void *cls,
+             const struct TALER_TESTING_Command *cmd,
+             struct TALER_TESTING_Interpreter *is)
+{
+  struct DepositState *ds = cls;
+  const struct TALER_TESTING_Command *coin_cmd;
+  struct TALER_TESTING_Command *this_cmd;
+  struct TALER_CoinSpendPrivateKeyP *coin_priv;
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+  struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
+  struct TALER_DenominationSignature *denom_pub_sig;
+  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 *merchant_priv;
+  struct TALER_MerchantPublicKeyP merchant_pub;
+  struct GNUNET_HashCode h_contract_terms;
+  json_t *contract_terms;
+  json_t *wire;
+  struct TALER_Amount amount;
+
+  ds->is = is;
+  this_cmd = &is->commands[is->ip];
+
+  GNUNET_assert (ds->coin_reference);
+  coin_cmd = TALER_TESTING_interpreter_lookup_command
+    (is,
+     ds->coin_reference);
+  if (NULL == coin_cmd)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);  
+    return;
+  }
+
+  /* Fixme: do prefer "interpreter fail" over assertions,
+   * as the former takes care of shutting down processes  too */
+  GNUNET_assert (NULL != coin_cmd);
+
+  GNUNET_assert (GNUNET_OK
+    == TALER_TESTING_get_trait_coin_priv (coin_cmd,
+                                          ds->coin_index,
+                                          &coin_priv));
+
+  GNUNET_assert (GNUNET_OK
+    == TALER_TESTING_get_trait_denom_pub (coin_cmd,
+                                          ds->coin_index,
+                                          &denom_pub));
+
+  GNUNET_assert (GNUNET_OK
+    == TALER_TESTING_get_trait_denom_sig (coin_cmd,
+                                          ds->coin_index,
+                                          &denom_pub_sig));
+  if (GNUNET_OK !=
+      TALER_string_to_amount (ds->amount,
+                              &amount))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to parse amount `%s' at '%u/%s'\n",
+                 ds->amount, is->ip, this_cmd->label);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  contract_terms = json_loads (ds->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",
+                ds->contract_terms, is->ip, this_cmd->label);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_JSON_hash (contract_terms,
+                                  &h_contract_terms));
+  json_decref (contract_terms);
+
+  wire = json_loads (ds->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",
+                ds->wire_details,
+                is->ip,
+                this_cmd->label);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  
+  GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
+                                      &coin_pub.eddsa_pub);
+
+  merchant_priv = GNUNET_CRYPTO_eddsa_key_create ();
+  ds->merchant_priv.eddsa_priv = *merchant_priv;
+  GNUNET_free (merchant_priv);
+
+  if (0 != ds->refund_deadline.rel_value_us)
+  {
+    refund_deadline = GNUNET_TIME_relative_to_absolute
+      (ds->refund_deadline);
+    wire_deadline = GNUNET_TIME_relative_to_absolute
+    (GNUNET_TIME_relative_multiply
+      (ds->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
+    (&ds->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_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, &denom_pub->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));
+  }
+  ds->dh = TALER_EXCHANGE_deposit
+    (ds->exchange,
+     &amount,
+     wire_deadline,
+     wire,
+     &h_contract_terms,
+     &coin_pub,
+     denom_pub_sig,
+     &denom_pub->key,
+     timestamp,
+     &merchant_pub,
+     refund_deadline,
+     &coin_sig,
+     &deposit_cb,
+     ds);
+
+  if (NULL == ds->dh)
+  {
+    GNUNET_break (0);
+    json_decref (wire);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  json_decref (wire);
+  return;
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+deposit_cleanup (void *cls,
+                 const struct TALER_TESTING_Command *cmd)
+{
+  struct DepositState *ds = cls;
+
+  if (NULL != ds->dh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Command %u (%s) did not complete\n",
+                ds->is->ip,
+                cmd->label);
+    TALER_EXCHANGE_deposit_cancel (ds->dh);
+    ds->dh = NULL;
+  }
+
+  GNUNET_free (ds->wire_details);
+  GNUNET_free (ds);
+}
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+deposit_traits (void *cls,
+                void **ret,
+                const char *trait,
+                unsigned int index)
+{
+  struct DepositState *ds = cls;
+  const struct TALER_TESTING_Command *coin_cmd; 
+  /* Will point to coin cmd internals. */
+  struct TALER_CoinSpendPrivateKeyP *coin_spent_priv;
+
+  coin_cmd = TALER_TESTING_interpreter_lookup_command
+    (ds->is, ds->coin_reference);
+
+  if (NULL == coin_cmd)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (ds->is);
+    return GNUNET_NO;
+  }
+
+  if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
+    (coin_cmd, ds->coin_index, &coin_spent_priv))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (ds->is);
+    return GNUNET_NO;
+  }
+
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_coin_priv (0, coin_spent_priv),
+    TALER_TESTING_make_trait_wire_details (0, ds->wire_details),
+    TALER_TESTING_make_trait_contract_terms (0, ds->contract_terms),
+    TALER_TESTING_make_trait_peer_key
+      (0, &ds->merchant_priv.eddsa_priv),
+    TALER_TESTING_trait_end ()  
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}
+
+/**
+ * Create a deposit command.
+ *
+ * @param label command label
+ * @param exchange exchange connection
+ * @param coin_reference reference to any operation that can
+ *        provide a coin
+ * @param coin_index if @a withdraw_reference offers an array of
+ *        coins, this parameter selects which one in that array.
+ *        This value is currently ignored, as only one-coin
+ *        withdrawals are implemented.
+ * @param wire_details bank details of the merchant performing the
+ *        deposit
+ * @param contract_terms contract terms to be signed over by the
+ *        coin
+ * @param refund_deadline refund deadline, zero means 'no refunds'
+ * @param amount how much is going to be deposited
+ * @param expected_response_code which HTTP status code we expect
+ *        in the response
+ *
+ * @return the deposit command to be run by the interpreter
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_deposit
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *coin_reference,
+   unsigned int coin_index,
+   char *wire_details,
+   const char *contract_terms,
+   struct GNUNET_TIME_Relative refund_deadline,
+   const char *amount,
+   unsigned int expected_response_code)
+{
+  struct TALER_TESTING_Command cmd;
+  struct DepositState *ds;
+  
+  ds = GNUNET_new (struct DepositState);
+  ds->exchange = exchange;
+  ds->coin_reference = coin_reference;
+  ds->coin_index = coin_index;
+  ds->wire_details = wire_details;
+  ds->contract_terms = contract_terms;
+  ds->refund_deadline = refund_deadline;
+  ds->amount = amount;
+  ds->expected_response_code = expected_response_code;
+
+  cmd.cls = ds;
+  cmd.label = label;
+  cmd.run = &deposit_run;
+  cmd.cleanup = &deposit_cleanup;
+  cmd.traits = &deposit_traits;
+
+  return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_exec_aggregator.c 
b/src/exchange-lib/testing_api_cmd_exec_aggregator.c
new file mode 100644
index 0000000..8d2131b
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_exec_aggregator.c
@@ -0,0 +1,162 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not,
+  see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_exec_aggregator.c
+ * @brief run the taler-exchange-aggregator command
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+
+struct AggregatorState
+{
+
+  /**
+   * Process for the aggregator.
+   */
+  struct GNUNET_OS_Process *aggregator_proc;
+
+  /**
+   * Which configuration file should we pass to the process?
+   */
+  const char *config_filename;
+
+};
+
+
+/**
+ * Runs the command.  Note that upon return, the interpreter
+ * will not automatically run the next command, as the command
+ * may continue asynchronously in other scheduler tasks.  Thus,
+ * the command must ensure to eventually call
+ * #TALER_TESTING_interpreter_next() or
+ * #TALER_TESTING_interpreter_fail().
+ *
+ * @param is interpreter state
+ */
+static void
+aggregator_run (void *cls,
+                const struct TALER_TESTING_Command *cmd,
+                struct TALER_TESTING_Interpreter *is)
+{
+  struct AggregatorState *as = cls;
+
+  as->aggregator_proc
+    = GNUNET_OS_start_process (GNUNET_NO,
+                               GNUNET_OS_INHERIT_STD_ALL,
+                               NULL, NULL, NULL,
+                               "taler-exchange-aggregator",
+                               "taler-exchange-aggregator",
+                               "-c", as->config_filename,
+                               "-t", /* exit when done */
+                               NULL);
+  if (NULL == as->aggregator_proc)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+/**
+ * Clean up after the command.  Run during forced termination
+ * (CTRL-C) or test failure or test success.
+ *
+ * @param cls closure
+ */
+static void
+aggregator_cleanup (void *cls,
+                    const struct TALER_TESTING_Command *cmd)
+{
+  struct AggregatorState *as = cls;
+
+  if (NULL != as->aggregator_proc)
+  {
+    GNUNET_break (0 ==
+                  GNUNET_OS_process_kill (as->aggregator_proc,
+                                          SIGKILL));
+    GNUNET_OS_process_wait (as->aggregator_proc);
+    GNUNET_OS_process_destroy (as->aggregator_proc);
+    as->aggregator_proc = NULL;
+  }
+  GNUNET_free (as);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+aggregator_traits (void *cls,
+                   void **ret,
+                   const char *trait,
+                   unsigned int index)
+{
+  struct AggregatorState *as = cls;
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_process (0, &as->aggregator_proc),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}
+
+
+/**
+ * Execute taler-exchange-wirewatch process.
+ *
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_aggregator (const char *label,
+                                   const char *config_filename)
+{
+  struct TALER_TESTING_Command cmd;
+  struct AggregatorState *as;
+
+  as = GNUNET_new (struct AggregatorState);
+  as->config_filename = config_filename;
+  cmd.cls = as;
+  cmd.label = label;
+  cmd.run = &aggregator_run;
+  cmd.cleanup = &aggregator_cleanup;
+  cmd.traits = &aggregator_traits;
+  return cmd;
+}
+
+/* end of testing_api_cmd_exec_aggregator.c */
diff --git a/src/exchange-lib/testing_api_cmd_exec_auditor-sign.c 
b/src/exchange-lib/testing_api_cmd_exec_auditor-sign.c
new file mode 100644
index 0000000..bae0c07
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_exec_auditor-sign.c
@@ -0,0 +1,221 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not,
+  see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_exec_auditor-sign.c
+ * @brief run the taler-exchange-aggregator command
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+
+struct AuditorSignState
+{
+
+  /**
+   * Process for the "auditor sign" command.
+   */
+  struct GNUNET_OS_Process *auditor_sign_proc;
+
+  /**
+   * Which configuration file should we pass to the process?
+   */
+  const char *config_filename;
+
+};
+
+
+/**
+ * Runs the command.  Note that upon return, the interpreter
+ * will not automatically run the next command, as the command
+ * may continue asynchronously in other scheduler tasks.  Thus,
+ * the command must ensure to eventually call
+ * #TALER_TESTING_interpreter_next() or
+ * #TALER_TESTING_interpreter_fail().
+ *
+ * @param is interpreter state
+ */
+static void
+auditor_sign_run (void *cls,
+                  const struct TALER_TESTING_Command *cmd,
+                  struct TALER_TESTING_Interpreter *is)
+{
+  struct AuditorSignState *ass = cls;
+
+  struct GNUNET_CONFIGURATION_Handle *cfg;
+  char *test_home_dir;
+  char *signed_keys_out;
+  char *exchange_master_pub;
+
+  cfg = GNUNET_CONFIGURATION_create ();
+  if (GNUNET_OK != GNUNET_CONFIGURATION_load
+    (cfg, ass->config_filename))
+  {
+    GNUNET_break (0); 
+    TALER_TESTING_interpreter_fail (is); 
+    return;
+  }
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             "paths",
+                                             "TALER_TEST_HOME",
+                                             &test_home_dir))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "paths",
+                               "TALER_TEST_HOME");
+    GNUNET_CONFIGURATION_destroy (cfg);
+    GNUNET_break (0); 
+    TALER_TESTING_interpreter_fail (is); 
+    return;
+  }
+
+  GNUNET_asprintf (&signed_keys_out,
+                   "%s/.local/share/taler/auditors/auditor.out",
+                   test_home_dir);
+
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             "exchange",
+                                             "MASTER_PUBLIC_KEY",
+                                             &exchange_master_pub))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "exchange",
+                               "MASTER_PUBLIC_KEY");
+    GNUNET_CONFIGURATION_destroy (cfg);
+
+    GNUNET_break (0); 
+    TALER_TESTING_interpreter_fail (is); 
+    return;
+  }
+
+  GNUNET_CONFIGURATION_destroy (cfg);
+
+  ass->auditor_sign_proc = GNUNET_OS_start_process
+    (GNUNET_NO,
+     GNUNET_OS_INHERIT_STD_ALL,
+     NULL, NULL, NULL,
+     "taler-auditor-sign",
+     "taler-auditor-sign",
+     "-c", ass->config_filename,
+     "-u", "http://auditor/";,
+     "-m", exchange_master_pub,
+     "-r", "auditor.in",
+     "-o", signed_keys_out,
+     NULL);
+
+  if (NULL == ass->auditor_sign_proc)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+/**
+ * Clean up after the command.  Run during forced termination
+ * (CTRL-C) or test failure or test success.
+ *
+ * @param cls closure
+ */
+static void
+auditor_sign_cleanup (void *cls,
+                    const struct TALER_TESTING_Command *cmd)
+{
+  struct AuditorSignState *ass = cls;
+
+  if (NULL != ass->auditor_sign_proc)
+  {
+    GNUNET_break (0 == GNUNET_OS_process_kill
+      (ass->auditor_sign_proc, SIGKILL));
+    GNUNET_OS_process_wait (ass->auditor_sign_proc);
+    GNUNET_OS_process_destroy (ass->auditor_sign_proc);
+    ass->auditor_sign_proc = NULL;
+  }
+  GNUNET_free (ass);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+auditor_sign_traits (void *cls,
+                     void **ret,
+                     const char *trait,
+                     unsigned int index)
+{
+  struct AuditorSignState *ass = cls;
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_process (0, &ass->auditor_sign_proc),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}
+
+
+/**
+ * Execute taler-auditor-sign process.
+ *
+ * @param label command label
+ * @param config_filename configuration filename
+ *
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_auditor_sign (const char *label,
+                                     const char *config_filename)
+{
+  struct TALER_TESTING_Command cmd;
+  struct AuditorSignState *ass;
+
+  ass = GNUNET_new (struct AuditorSignState);
+  ass->config_filename = config_filename;
+  cmd.cls = ass;
+  cmd.label = label;
+  cmd.run = &auditor_sign_run;
+  cmd.cleanup = &auditor_sign_cleanup;
+  cmd.traits = &auditor_sign_traits;
+  return cmd;
+}
+
+/* end of testing_api_cmd_exec_auditor-sign.c */
diff --git a/src/exchange-lib/testing_api_cmd_exec_keyup.c 
b/src/exchange-lib/testing_api_cmd_exec_keyup.c
new file mode 100644
index 0000000..9398dd4
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_exec_keyup.c
@@ -0,0 +1,168 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not,
+  see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_exec_keyup.c
+ * @brief run the taler-exchange-keyup command
+ * @author Marcello Stanisci
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+
+struct KeyupState
+{
+
+  /**
+   * Process for the "keyup" command.
+   */
+  struct GNUNET_OS_Process *keyup_proc;
+
+  /**
+   * Which configuration file should we pass to the process?
+   */
+  const char *config_filename;
+
+};
+
+
+/**
+ * Runs the command.  Note that upon return, the interpreter
+ * will not automatically run the next command, as the command
+ * may continue asynchronously in other scheduler tasks.  Thus,
+ * the command must ensure to eventually call
+ * #TALER_TESTING_interpreter_next() or
+ * #TALER_TESTING_interpreter_fail().
+ *
+ * @param is interpreter state
+ */
+static void
+keyup_run (void *cls,
+           const struct TALER_TESTING_Command *cmd,
+           struct TALER_TESTING_Interpreter *is)
+{
+  struct KeyupState *ks = cls;
+
+  ks->keyup_proc = GNUNET_OS_start_process
+    (GNUNET_NO,
+     GNUNET_OS_INHERIT_STD_ALL,
+     NULL, NULL, NULL,
+     "taler-exchange-keyup",
+     "taler-exchange-keyup",
+     "-c", ks->config_filename,
+     "-o", "auditor.in",
+     NULL);
+
+  if (NULL == ks->keyup_proc)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+/**
+ * Clean up after the command.  Run during forced termination
+ * (CTRL-C) or test failure or test success.
+ *
+ * @param cls closure
+ */
+static void
+keyup_cleanup (void *cls,
+               const struct TALER_TESTING_Command *cmd)
+{
+  struct KeyupState *ks = cls;
+
+  if (NULL != ks->keyup_proc)
+  {
+    GNUNET_break (0 ==
+                  GNUNET_OS_process_kill (ks->keyup_proc,
+                                          SIGKILL));
+    GNUNET_OS_process_wait (ks->keyup_proc);
+    GNUNET_OS_process_destroy (ks->keyup_proc);
+    ks->keyup_proc = NULL;
+  }
+  GNUNET_free (ks);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+keyup_traits (void *cls,
+              void **ret,
+              const char *trait,
+              unsigned int index)
+{
+  struct KeyupState *ks = cls;
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_process (0, &ks->keyup_proc),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}
+
+
+/**
+ * Execute taler-exchange-keyup process.
+ *
+ * @param label command label
+ * @param config_filename configuration filename
+ *
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_keyup (const char *label,
+                              const char *config_filename)
+{
+  struct TALER_TESTING_Command cmd;
+  struct KeyupState *ks;
+
+  ks = GNUNET_new (struct KeyupState);
+  ks->config_filename = config_filename;
+  cmd.cls = ks;
+  cmd.label = label;
+  cmd.run = &keyup_run;
+  cmd.cleanup = &keyup_cleanup;
+  cmd.traits = &keyup_traits;
+  return cmd;
+}
+
+/* end of testing_api_cmd_exec_keyup.c */
diff --git a/src/exchange-lib/testing_api_cmd_exec_wirewatch.c 
b/src/exchange-lib/testing_api_cmd_exec_wirewatch.c
index 7b95afb..107ac55 100644
--- a/src/exchange-lib/testing_api_cmd_exec_wirewatch.c
+++ b/src/exchange-lib/testing_api_cmd_exec_wirewatch.c
@@ -122,11 +122,11 @@ static int
 wirewatch_traits (void *cls,
                   void **ret,
                   const char *trait,
-                  const char *selector)
+                  unsigned int index)
 {
   struct WirewatchState *ws = cls;
   struct TALER_TESTING_Trait traits[] = {
-    TALER_TESTING_make_trait_process (NULL,
+    TALER_TESTING_make_trait_process (0,
                                       &ws->wirewatch_proc),
     TALER_TESTING_trait_end ()
   };
@@ -134,7 +134,7 @@ wirewatch_traits (void *cls,
   return TALER_TESTING_get_trait (traits,
                                   ret,
                                   trait,
-                                  selector);
+                                  index);
 }
 
 
diff --git a/src/exchange-lib/testing_api_cmd_fakebank_transfer.c 
b/src/exchange-lib/testing_api_cmd_fakebank_transfer.c
index 6b1a99d..dfbaadd 100644
--- a/src/exchange-lib/testing_api_cmd_fakebank_transfer.c
+++ b/src/exchange-lib/testing_api_cmd_fakebank_transfer.c
@@ -2,18 +2,21 @@
   This file is part of TALER
   Copyright (C) 2018 Taler Systems SA
 
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along with
-  TALER; see the file COPYING.  If not, see
+  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-lib/testing_api_cmd_fakebank_transfer.c
  * @brief implementation of a fakebank wire transfer command
@@ -104,14 +107,17 @@ struct FakebankTransferState
 
 
 /**
- * Function called upon completion of our /admin/add/incoming request.
+ * 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 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)
+ * @param full_response full response from the exchange (for
+ *        logging, in case of errors)
  */
 static void
 add_incoming_cb (void *cls,
@@ -167,8 +173,8 @@ fakebank_transfer_run (void *cls,
       const struct TALER_TESTING_Command *ref;
       struct TALER_ReservePrivateKeyP *reserve_priv;
 
-      ref = TALER_TESTING_interpreter_lookup_command (is,
-                                                      fts->reserve_reference);
+      ref = TALER_TESTING_interpreter_lookup_command
+        (is, fts->reserve_reference);
       if (NULL == ref)
       {
         GNUNET_break (0);
@@ -177,7 +183,7 @@ fakebank_transfer_run (void *cls,
       }
       if (GNUNET_OK !=
           TALER_TESTING_get_trait_reserve_priv (ref,
-                                                NULL,
+                                                0,
                                                 &reserve_priv))
       {
         GNUNET_break (0);
@@ -193,27 +199,27 @@ fakebank_transfer_run (void *cls,
       fts->reserve_priv.eddsa_priv = *priv;
       GNUNET_free (priv);
     }
-    GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv,
-                                        &reserve_pub.eddsa_pub);
-    subject = GNUNET_STRINGS_data_to_string_alloc (&reserve_pub,
-                                                   sizeof (reserve_pub));
+    GNUNET_CRYPTO_eddsa_key_get_public
+      (&fts->reserve_priv.eddsa_priv, &reserve_pub.eddsa_pub);
+    subject = GNUNET_STRINGS_data_to_string_alloc
+      (&reserve_pub, sizeof (reserve_pub));
   }
 
   auth.method = TALER_BANK_AUTH_BASIC;
   auth.details.basic.username = (char *) fts->auth_username;
   auth.details.basic.password = (char *) fts->auth_password;
   fts->is = is;
-  fts->aih
-    = TALER_BANK_admin_add_incoming (TALER_TESTING_interpreter_get_context 
(is),
-                                     fts->bank_url,
-                                     &auth,
-                                     "https://exchange.com/";, /* exchange URL: 
FIXME */
-                                     subject,
-                                     &fts->amount,
-                                     fts->debit_account_no,
-                                     fts->credit_account_no,
-                                     &add_incoming_cb,
-                                     fts);
+  fts->aih = TALER_BANK_admin_add_incoming
+    (TALER_TESTING_interpreter_get_context (is),
+     fts->bank_url,
+     &auth,
+     "https://exchange.com/";, /* exchange URL: FIXME */
+     subject,
+     &fts->amount,
+     fts->debit_account_no,
+     fts->credit_account_no,
+     &add_incoming_cb,
+     fts);
   GNUNET_free (subject);
   if (NULL == fts->aih)
   {
@@ -263,11 +269,11 @@ static int
 fakebank_transfer_traits (void *cls,
                           void **ret,
                           const char *trait,
-                          const char *selector)
+                          unsigned int index)
 {
   struct FakebankTransferState *fts = cls;
   struct TALER_TESTING_Trait traits[] = {
-    TALER_TESTING_make_trait_reserve_priv (NULL,
+    TALER_TESTING_make_trait_reserve_priv (0,
                                            &fts->reserve_priv),
     TALER_TESTING_trait_end ()
   };
@@ -275,12 +281,13 @@ fakebank_transfer_traits (void *cls,
   if (NULL != fts->subject)
   {
     GNUNET_break (0);
-    return GNUNET_SYSERR; /* we do NOT create a reserve private key */
+    /* we do NOT create a reserve private key */
+    return GNUNET_SYSERR; 
   }
   return TALER_TESTING_get_trait (traits,
                                   ret,
                                   trait,
-                                  selector);
+                                  index);
 }
 
 
@@ -330,14 +337,15 @@ TALER_TESTING_cmd_fakebank_transfer (const char *label,
  *
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label,
-                                                  const char *amount,
-                                                  const char *bank_url,
-                                                  uint64_t debit_account_no,
-                                                  uint64_t credit_account_no,
-                                                  const char *auth_username,
-                                                  const char *auth_password,
-                                                  const char *subject)
+TALER_TESTING_cmd_fakebank_transfer_with_subject
+  (const char *label,
+   const char *amount,
+   const char *bank_url,
+   uint64_t debit_account_no,
+   uint64_t credit_account_no,
+   const char *auth_username,
+   const char *auth_password,
+   const char *subject)
 {
   struct TALER_TESTING_Command cmd;
   struct FakebankTransferState *fts;
@@ -373,14 +381,15 @@ TALER_TESTING_cmd_fakebank_transfer_with_subject (const 
char *label,
  *
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label,
-                                              const char *amount,
-                                              const char *bank_url,
-                                              uint64_t debit_account_no,
-                                              uint64_t credit_account_no,
-                                              const char *auth_username,
-                                              const char *auth_password,
-                                              const char *ref)
+TALER_TESTING_cmd_fakebank_transfer_with_ref
+  (const char *label,
+   const char *amount,
+   const char *bank_url,
+   uint64_t debit_account_no,
+   uint64_t credit_account_no,
+   const char *auth_username,
+   const char *auth_password,
+   const char *ref)
 {
   struct TALER_TESTING_Command cmd;
   struct FakebankTransferState *fts;
diff --git a/src/exchange-lib/testing_api_cmd_payback.c 
b/src/exchange-lib/testing_api_cmd_payback.c
new file mode 100644
index 0000000..6c791e5
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_payback.c
@@ -0,0 +1,491 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 3, or
+  (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange/testing_api_cmd_payback.c
+ * @brief Implement the /revoke and /payback test commands.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct RevokeState
+{
+  /** 
+   * Expected HTTP status code.
+   */
+  unsigned int expected_response_code;
+
+  /**
+   * Command that offers a denomination to revoke.
+   */
+  const char *coin_reference;
+
+  /**
+   * The interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+
+  /**
+   * The revoke process handle.
+   */
+  struct GNUNET_OS_Process *revoke_proc;
+
+  /**
+   * Configuration filename.
+   */
+  const char *config_filename;
+
+  /**
+   * Encoding of the denomination (to revoke) public key hash.
+   */
+  char *dhks;
+
+};
+
+struct PaybackState
+{
+  /** 
+   * Expected HTTP status code.
+   */
+  unsigned int expected_response_code;
+
+  /**
+   * Command that offers a reserve private key plus a
+   * coin to be paid back.
+   */
+  const char *coin_reference;
+
+  /**
+   * The interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+
+  /**
+   * Amount expected to be paid back.
+   */
+  const char *amount;
+
+  /**
+   * Connection to the exchange.
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * Handle to the ongoing operation.
+   */
+  struct TALER_EXCHANGE_PaybackHandle *ph;
+
+};
+
+/**
+ * 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 PaybackState *ps = cls;
+  struct TALER_TESTING_Interpreter *is = ps->is;
+  struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
+  const struct TALER_TESTING_Command *reserve_cmd;
+  struct TALER_ReservePrivateKeyP *reserve_priv;
+  struct TALER_ReservePublicKeyP rp;
+  struct TALER_Amount expected_amount;
+
+  ps->ph = NULL;
+  if (ps->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);
+    fprintf (stderr, "\n");
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+
+  reserve_cmd = TALER_TESTING_interpreter_lookup_command
+    (is, ps->coin_reference);
+
+  if (NULL == reserve_cmd)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  
+  if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv
+    (reserve_cmd, 0, &reserve_priv))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+
+  GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
+                                      &rp.eddsa_pub);
+
+  switch (http_status)
+  {
+  case MHD_HTTP_OK:
+    if (GNUNET_OK != TALER_string_to_amount
+      (ps->amount, &expected_amount))
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_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);
+      TALER_TESTING_interpreter_fail (is);
+      return;
+    }
+    if (0 != memcmp (reserve_pub, &rp,
+                     sizeof (struct TALER_ReservePublicKeyP)))
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (is);
+      return;
+    }
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Unmanaged HTTP status code.\n");
+    break;
+  }
+  TALER_TESTING_interpreter_next (is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param is the interpreter state.
+ */
+void
+payback_run (void *cls,
+             const struct TALER_TESTING_Command *cmd,
+             struct TALER_TESTING_Interpreter *is)
+{
+  struct PaybackState *ps = cls;
+  const struct TALER_TESTING_Command *coin_cmd;
+  struct TALER_CoinSpendPrivateKeyP *coin_priv;
+  struct TALER_DenominationBlindingKeyP *blinding_key;
+  struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
+  struct TALER_DenominationSignature *coin_sig;
+  struct TALER_PlanchetSecretsP planchet;
+
+  ps->is = is;
+  ps->exchange = is->exchange;
+  coin_cmd = TALER_TESTING_interpreter_lookup_command
+    (is, ps->coin_reference);
+
+  if (NULL == coin_cmd)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;  
+  }
+
+  if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
+    (coin_cmd, 0, &coin_priv))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return; 
+  }
+
+  if (GNUNET_OK != TALER_TESTING_get_trait_blinding_key
+    (coin_cmd, 0, &blinding_key))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return; 
+  }
+  planchet.coin_priv = *coin_priv;
+  planchet.blinding_key = *blinding_key;
+
+  if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub
+    (coin_cmd, 0, &denom_pub))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return; 
+  }
+
+  if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig
+     (coin_cmd, 0, &coin_sig))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return; 
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Trying to get '%s..' paid back\n",
+              TALER_B2S (&denom_pub->h_key));
+  
+  ps->ph = TALER_EXCHANGE_payback (ps->exchange,
+                                   denom_pub,
+                                   coin_sig,
+                                   &planchet,
+                                   payback_cb,
+                                   ps);
+  GNUNET_assert (NULL != ps->ph);
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+revoke_cleanup (void *cls,
+                const struct TALER_TESTING_Command *cmd)
+{
+
+  struct RevokeState *rs = cls;
+
+  if (NULL != rs->revoke_proc)
+  {
+    GNUNET_break (0 == GNUNET_OS_process_kill
+      (rs->revoke_proc, SIGKILL));
+    GNUNET_OS_process_wait (rs->revoke_proc);
+    GNUNET_OS_process_destroy (rs->revoke_proc);
+    rs->revoke_proc = NULL;
+  }
+
+  GNUNET_free (rs->dhks);
+  GNUNET_free (rs);
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+payback_cleanup (void *cls,
+                 const struct TALER_TESTING_Command *cmd)
+{
+  struct PaybackState *ps = cls;
+  if (NULL != ps->ph)
+  {
+    TALER_EXCHANGE_payback_cancel (ps->ph);
+    ps->ph = NULL;
+  }
+  GNUNET_free (ps);
+}
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+revoke_traits (void *cls,
+               void **ret,
+               const char *trait,
+               unsigned int index)
+{
+
+  struct RevokeState *rs = cls;
+
+  struct TALER_TESTING_Trait traits[] = {
+    /* Needed by the handler which waits the proc'
+     * death and calls the next command */
+    TALER_TESTING_make_trait_process (0, &rs->revoke_proc),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param is the interpreter state.
+ */
+void
+revoke_run (void *cls,
+            const struct TALER_TESTING_Command *cmd,
+            struct TALER_TESTING_Interpreter *is)
+{
+  struct RevokeState *rs = cls;
+  const struct TALER_TESTING_Command *coin_cmd;
+  struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
+
+  rs->is = is;
+  /* Get denom pub from trait */
+  coin_cmd = TALER_TESTING_interpreter_lookup_command
+    (is, rs->coin_reference);
+
+  if (NULL == coin_cmd)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;  
+  }
+
+  GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_denom_pub
+    (coin_cmd, 0, &denom_pub));
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Trying to revoke denom '%s..'\n",
+              TALER_B2S (&denom_pub->h_key));
+
+  rs->dhks = GNUNET_STRINGS_data_to_string_alloc
+    (&denom_pub->h_key, sizeof (struct GNUNET_HashCode)); 
+  
+  rs->revoke_proc = GNUNET_OS_start_process
+    (GNUNET_NO,
+     GNUNET_OS_INHERIT_STD_ALL,
+     NULL, NULL, NULL,
+     "taler-exchange-keyup",
+     "taler-exchange-keyup",
+     "-c", rs->config_filename,
+     "-r", rs->dhks,
+     NULL);
+
+
+  if (NULL == rs->revoke_proc)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Revoke is ongoing..\n");
+
+  is->reload_keys = GNUNET_OK;
+  TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+/**
+ * Make a /payback command.
+ *
+ * @param label the command label
+ * @param expected_response_code expected HTTP status code
+ * @param coin_reference reference to any command which
+ *        offers a reserve private key
+ * @param amount denomination to pay back.
+ *
+ * @return a /revoke command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_payback (const char *label,
+                           unsigned int expected_response_code,
+                           const char *coin_reference,
+                           const char *amount)
+{
+  struct PaybackState *ps;
+  struct TALER_TESTING_Command cmd;
+  
+  ps = GNUNET_new (struct PaybackState);
+  ps->expected_response_code = expected_response_code;
+  ps->coin_reference = coin_reference;
+  ps->amount = amount;
+
+  cmd.cls = ps;
+  cmd.label = label;
+  cmd.run = &payback_run;
+  cmd.cleanup = &payback_cleanup;
+
+  return cmd;
+}
+
+/**
+ * Make a /revoke command.
+ *
+ * @param label the command label
+ * @param expected_response_code expected HTTP status code
+ * @param coin_reference reference to any command which offers
+ *        a coin trait
+ * @param config_filename configuration file name.
+ *
+ * @return a /revoke command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_revoke (const char *label,
+                          unsigned int expected_response_code,
+                          const char *coin_reference,
+                          const char *config_filename)
+{
+
+  struct RevokeState *rs;
+  struct TALER_TESTING_Command cmd;
+
+  rs = GNUNET_new (struct RevokeState);
+  rs->expected_response_code = expected_response_code;
+  rs->coin_reference = coin_reference;
+  rs->config_filename = config_filename;
+
+  cmd.cls = rs;
+  cmd.label = label;
+  cmd.run = &revoke_run;
+  cmd.cleanup = &revoke_cleanup;
+  cmd.traits = &revoke_traits;
+
+  return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_refresh.c 
b/src/exchange-lib/testing_api_cmd_refresh.c
new file mode 100644
index 0000000..3e58552
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_refresh.c
@@ -0,0 +1,993 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_refresh.c
+ * @brief commands for testing all "refresh" features.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+#include "taler_signatures.h"
+
+/**
+ * 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_reference;
+};
+
+
+/**
+ * State for a "refresh melt" operation.
+ */
+struct RefreshMeltState
+{
+  /**
+   * Fixme: figure out this data purpose.
+   */
+  const char *amount;
+
+  /**
+   * Information about coins to be melted.
+   */
+  struct MeltDetails melted_coin;
+
+  /**
+   * Data used in the refresh operation.
+   */
+  char *refresh_data;
+
+  /**
+   * Number of bytes in @e refresh_data.
+   */
+  size_t refresh_data_length;
+
+  /**
+   * Reference to a previous melt command.
+   */
+  const char *melt_reference;
+
+  /**
+   * Melt handle while operation is running.
+   */
+  struct TALER_EXCHANGE_RefreshMeltHandle *rmh;
+
+  /**
+   * Connection to the exchange.
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * Interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+
+  /**
+   * Expected HTTP response code.
+   */
+  unsigned int expected_response_code;
+
+  /**
+   * Array of the public keys corresponding to
+   * the @e fresh_amounts, set by the interpreter.
+   */
+  struct TALER_EXCHANGE_DenomPublicKey *fresh_pks;
+
+  /**
+   * Set by the interpreter (upon completion) to the
+   * noreveal index selected by the exchange.
+   */
+  uint16_t noreveal_index;
+};
+
+/**
+ * State for a "refresh reveal" operation.
+ */
+struct RefreshRevealState
+{
+  /**
+   * Link to a "refresh melt" command.
+   */
+  const char *melt_reference;
+
+  /**
+   * 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;
+
+  /**
+   * Connection to the exchange.
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * Interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+
+  /**
+   * Expected HTTP response code.
+   */
+  unsigned int expected_response_code;
+};
+
+/**
+ * State for a "refresh link" operation.
+ */
+struct RefreshLinkState
+{
+  /**
+   * Link to a "refresh reveal" command.
+   */
+  const char *reveal_reference;
+
+  /**
+   * Link handle while operation is running.
+   */
+  struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
+
+  /**
+   * Connection to the exchange.
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * Interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+
+  /**
+   * Expected HTTP response code.
+   */
+  unsigned int expected_response_code;
+};
+
+
+/**
+ * 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 RefreshRevealState *rrs = cls;
+  const struct TALER_TESTING_Command *melt_cmd;
+
+  rrs->rrh = NULL;
+  if (rrs->expected_response_code != http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u to command %s\n",
+                http_status,
+                rrs->is->commands[rrs->is->ip].label);
+    json_dumpf (full_response, stderr, 0);
+    TALER_TESTING_interpreter_fail (rrs->is);
+    return;
+  }
+  melt_cmd = TALER_TESTING_interpreter_lookup_command
+    (rrs->is, rrs->melt_reference);
+  if (NULL == melt_cmd)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (rrs->is);
+    return;
+  }
+  rrs->num_fresh_coins = num_coins;
+  switch (http_status)
+  {
+  case MHD_HTTP_OK:
+    rrs->fresh_coins = GNUNET_new_array
+      (num_coins, struct FreshCoin);
+
+    struct TALER_EXCHANGE_DenomPublicKey *fresh_pks;
+    unsigned int i;
+    if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub
+      (melt_cmd, 0, &fresh_pks))
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (rrs->is);
+      return;    
+    }
+
+    for (i=0; i<num_coins; i++)
+    {
+      struct FreshCoin *fc = &rrs->fresh_coins[i];
+
+      fc->pk = &fresh_pks[i];
+      fc->coin_priv = coin_privs[i];
+      fc->sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup
+        (sigs[i].rsa_signature);
+    }
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Unknown HTTP status %d\n",
+                http_status);
+  }
+  TALER_TESTING_interpreter_next (rrs->is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure.
+ * @param cmd the command to execute, a /wire one.
+ * @param is the interpreter state.
+ */
+void
+refresh_reveal_run (void *cls,
+                    const struct TALER_TESTING_Command *cmd,
+                    struct TALER_TESTING_Interpreter *is)
+{
+  struct RefreshRevealState *rrs = cls;
+  struct RefreshMeltState *rms;
+  const struct TALER_TESTING_Command *melt_cmd;
+    
+  rrs->is = is;
+  melt_cmd = TALER_TESTING_interpreter_lookup_command
+    (is, rrs->melt_reference);
+  
+  if (NULL == melt_cmd)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (rrs->is);
+    return;
+  }
+  rms = melt_cmd->cls;
+  rrs->rrh = TALER_EXCHANGE_refresh_reveal
+    (rrs->exchange,
+     rms->refresh_data_length,
+     rms->refresh_data,
+     rms->noreveal_index,
+     &reveal_cb, rrs);
+
+  if (NULL == rrs->rrh)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+}
+
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+refresh_reveal_cleanup (void *cls,
+                        const struct TALER_TESTING_Command *cmd)
+{
+  struct RefreshRevealState *rrs = cls; 
+
+  if (NULL != rrs->rrh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Command %u (%s) did not complete\n",
+                rrs->is->ip,
+                cmd->label);
+
+    TALER_EXCHANGE_refresh_reveal_cancel (rrs->rrh);
+    rrs->rrh = NULL;
+  }
+
+  { /* Not sure why block-ing this */
+    unsigned int j;
+
+    for (j=0; j < rrs->num_fresh_coins; j++)
+      GNUNET_CRYPTO_rsa_signature_free
+        (rrs->fresh_coins[j].sig.rsa_signature);
+  }
+
+  GNUNET_free_non_null (rrs->fresh_coins);
+  rrs->fresh_coins = NULL;
+  rrs->num_fresh_coins = 0;
+}
+
+
+/**
+ * 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 RefreshLinkState *rls = cls;
+  const struct TALER_TESTING_Command *reveal_cmd;
+  struct TALER_TESTING_Command *link_cmd
+    = &rls->is->commands[rls->is->ip];
+  unsigned int found;
+  unsigned int *num_fresh_coins;
+
+  rls->rlh = NULL;
+  if (rls->expected_response_code != http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u to command %s\n",
+                http_status,
+                link_cmd->label);
+    json_dumpf (full_response, stderr, 0);
+    TALER_TESTING_interpreter_fail (rls->is);
+    return;
+  }
+  reveal_cmd = TALER_TESTING_interpreter_lookup_command
+    (rls->is, rls->reveal_reference);
+
+  if (NULL == reveal_cmd)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (rls->is);
+    return;
+  }
+
+  switch (http_status)
+  {
+  case MHD_HTTP_OK:
+    /* check that number of coins returned matches */
+    if (GNUNET_OK != TALER_TESTING_get_trait_uint
+      (reveal_cmd, 0, &num_fresh_coins))
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (rls->is);
+      return;
+    }
+    if (num_coins != *num_fresh_coins)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Unexpected number of fresh coins: %d vs %d\n",
+                  num_coins, *num_fresh_coins);
+      TALER_TESTING_interpreter_fail (rls->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;
+
+    /* Will point to the pointer inside the cmd state. */
+    struct FreshCoin *fc = NULL;
+
+    if (GNUNET_OK != TALER_TESTING_get_trait_fresh_coins
+      (reveal_cmd, 0, &fc))
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (rls->is);
+      return;
+    }
+
+    for (unsigned int i=0;i<num_coins;i++)
+      for (unsigned int j=0;j<num_coins;j++)
+      {
+       if ( (0 == memcmp
+                (&coin_privs[i], &fc[i].coin_priv,
+                sizeof (struct TALER_CoinSpendPrivateKeyP))) &&
+            (0 == GNUNET_CRYPTO_rsa_signature_cmp
+                (fc[i].sig.rsa_signature,
+                 sigs[i].rsa_signature)) &&
+            (0 == GNUNET_CRYPTO_rsa_public_key_cmp
+               (fc[i].pk->key.rsa_public_key,
+                pubs[i].rsa_public_key)) )
+       {
+         found++;
+         break;
+       }
+      }
+    if (found != num_coins)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Only %u/%u coins match expectations\n",
+                 found, num_coins);
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (rls->is);
+      return;
+    }
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unknown HTTP response code %u.\n",
+                http_status);
+  }
+  TALER_TESTING_interpreter_next (rls->is);
+}
+
+
+/**
+ * Run the command.
+ *
+ * @param cls closure
+ * @param cmd the command to execute, a /wire one.
+ * @param i the interpreter state.
+ */
+void
+refresh_link_run (void *cls,
+                  const struct TALER_TESTING_Command *cmd,
+                  struct TALER_TESTING_Interpreter *is)
+{
+  
+  struct RefreshLinkState *rls = cls;
+  struct RefreshRevealState *rrs;
+  struct RefreshMeltState *rms;
+
+  const struct TALER_TESTING_Command *reveal_cmd;
+  const struct TALER_TESTING_Command *melt_cmd;
+  const struct TALER_TESTING_Command *coin_cmd;
+  rls->is = is;
+
+  reveal_cmd = TALER_TESTING_interpreter_lookup_command
+    (rls->is, rls->reveal_reference);
+
+  if (NULL == reveal_cmd)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (rls->is);
+    return; 
+  }
+  rrs = reveal_cmd->cls;
+  melt_cmd = TALER_TESTING_interpreter_lookup_command
+    (rls->is, rrs->melt_reference);
+
+  if (NULL == melt_cmd)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (rls->is);
+    return; 
+  }
+
+  /* find reserve_withdraw command */
+  {
+    const struct MeltDetails *md;
+    
+    rms = melt_cmd->cls;
+    md = &rms->melted_coin;
+    coin_cmd = TALER_TESTING_interpreter_lookup_command
+      (rls->is, md->coin_reference);
+    if (NULL == coin_cmd)
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (rls->is);
+      return;
+    }
+  }
+
+  struct TALER_CoinSpendPrivateKeyP *coin_priv;
+  if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
+    (coin_cmd, 0, &coin_priv))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (rls->is);
+    return;
+  }
+
+  /* finally, use private key from withdraw sign command */
+  rls->rlh = TALER_EXCHANGE_refresh_link
+    (rls->exchange, coin_priv, &link_cb, rls);
+
+  if (NULL == rls->rlh)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (rls->is);
+    return;
+  }
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure
+ * @param cmd the command which is being cleaned up.
+ */
+void
+refresh_link_cleanup (void *cls,
+                      const struct TALER_TESTING_Command *cmd)
+{
+  struct RefreshLinkState *rls = cls;
+  
+  if (NULL != rls->rlh)
+  {
+
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Command %u (%s) did not complete\n",
+                rls->is->ip,
+                cmd->label);
+    TALER_EXCHANGE_refresh_link_cancel (rls->rlh);
+    rls->rlh = NULL;
+  }
+}
+
+
+/**
+ * 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 RefreshMeltState *rms = cls;
+
+  rms->rmh = NULL;
+  if (rms->expected_response_code != http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u to command %s\n",
+                http_status,
+                rms->is->commands[rms->is->ip].label);
+    json_dumpf (full_response, stderr, 0);
+    TALER_TESTING_interpreter_fail (rms->is);
+    return;
+  }
+  rms->noreveal_index = noreveal_index;
+  TALER_TESTING_interpreter_next (rms->is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param i the interpreter state.
+ */
+void
+refresh_melt_run (void *cls,
+                  const struct TALER_TESTING_Command *cmd,
+                  struct TALER_TESTING_Interpreter *is)
+{
+  struct RefreshMeltState *rms = cls;
+  unsigned int num_fresh_coins;
+  const struct TALER_TESTING_Command *coin_command;
+  const char *melt_fresh_amounts[] = {
+    /* 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 */
+    "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", NULL};
+  const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk;
+
+  rms->is = is;
+  rms->noreveal_index = UINT16_MAX;
+  for (num_fresh_coins=0;
+       NULL != melt_fresh_amounts[num_fresh_coins];
+       num_fresh_coins++) ;
+
+  rms->fresh_pks = GNUNET_new_array
+    (num_fresh_coins,
+     struct TALER_EXCHANGE_DenomPublicKey);
+  {
+    struct TALER_CoinSpendPrivateKeyP *melt_priv;
+    struct TALER_Amount melt_amount;
+    struct TALER_Amount fresh_amount;
+    struct TALER_DenominationSignature *melt_sig;
+    struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub;
+    unsigned int i;
+
+    const struct MeltDetails *md = &rms->melted_coin;
+    if (NULL == (coin_command
+      = TALER_TESTING_interpreter_lookup_command
+        (is, md->coin_reference)))
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (rms->is);
+      return; 
+    }
+
+    if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
+      (coin_command, 0, &melt_priv))
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (rms->is);
+      return;    
+    }
+
+    if (GNUNET_OK !=
+        TALER_string_to_amount (md->amount,
+                                &melt_amount))
+    {
+      GNUNET_break (0);
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Failed to parse amount `%s' at %u\n",
+                  md->amount,
+                  is->ip);
+      TALER_TESTING_interpreter_fail (rms->is);
+      return;
+    }
+    if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig
+      (coin_command, 0, &melt_sig))
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (rms->is);
+      return;
+    }
+    if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub
+      (coin_command, 0, &melt_denom_pub))
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (rms->is);
+      return;
+    }
+
+    for (i=0;i<num_fresh_coins;i++)
+    {
+      if (GNUNET_OK != TALER_string_to_amount
+        (melt_fresh_amounts[i], &fresh_amount))
+      {
+        GNUNET_break (0);
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Failed to parse amount `%s' at index %u\n",
+                    melt_fresh_amounts[i], i);
+        TALER_TESTING_interpreter_fail (rms->is);
+        return;
+      }
+      fresh_pk = TALER_TESTING_find_pk
+        (TALER_EXCHANGE_get_keys (rms->exchange), &fresh_amount);
+      if (NULL == fresh_pk)
+      {
+        GNUNET_break (0);
+        /* Subroutine logs specific error */
+        TALER_TESTING_interpreter_fail (rms->is);
+        return;
+      }
+
+      rms->fresh_pks[i] = *fresh_pk;
+    }
+    rms->refresh_data = TALER_EXCHANGE_refresh_prepare
+      (melt_priv, &melt_amount, melt_sig, melt_denom_pub,
+       GNUNET_YES, num_fresh_coins, rms->fresh_pks,
+       &rms->refresh_data_length);
+
+    if (NULL == rms->refresh_data)
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (rms->is);
+      return;
+    }
+    rms->rmh = TALER_EXCHANGE_refresh_melt
+      (rms->exchange, rms->refresh_data_length,
+       rms->refresh_data, &melt_cb, rms);
+
+    if (NULL == rms->rmh)
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (rms->is);
+      return;
+    }
+  }
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct RefreshMeltState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+refresh_melt_cleanup (void *cls,
+                      const struct TALER_TESTING_Command *cmd)
+{
+  struct RefreshMeltState *rms = cls;
+
+  if (NULL != rms->rmh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Command %u (%s) did not complete\n",
+                rms->is->ip, rms->is->commands[rms->is->ip].label);
+    TALER_EXCHANGE_refresh_melt_cancel (rms->rmh);
+    rms->rmh = NULL;
+  }
+  GNUNET_free_non_null (rms->fresh_pks);
+  rms->fresh_pks = NULL;
+  GNUNET_free_non_null (rms->refresh_data);
+  rms->refresh_data = NULL;
+  rms->refresh_data_length = 0;
+}
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *        to return in case there were multiple generated
+ *        by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+refresh_melt_traits (void *cls,
+                     void **ret,
+                     const char *trait,
+                     unsigned int index)
+{
+  struct RefreshMeltState *rms = cls;
+
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_denom_pub (0, rms->fresh_pks),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}
+
+
+/**
+ * Create a "refresh melt" command.
+ *
+ * @param label command label
+ * @param exchange connection to the exchange
+ * @param amount Fixme
+ * @param coin_reference reference to a command that will provide
+ *        a coin to refresh
+ * @param expected_response_code expected HTTP code
+ */
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_refresh_melt
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *amount,
+   const char *coin_reference,
+   unsigned int expected_response_code)
+{
+  struct RefreshMeltState *rms;
+  struct MeltDetails md;
+  struct TALER_TESTING_Command cmd;
+
+  md.coin_reference = coin_reference;
+  md.amount = amount;
+
+  rms = GNUNET_new (struct RefreshMeltState);
+  rms->amount = amount;
+  rms->melted_coin = md;
+  rms->expected_response_code = expected_response_code;
+  rms->exchange = exchange;
+
+  cmd.label = label;
+  cmd.cls = rms;
+  cmd.run = &refresh_melt_run;
+  cmd.cleanup = &refresh_melt_cleanup;
+  cmd.traits = &refresh_melt_traits;
+  
+  return cmd;
+}
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *        to return in case there were multiple generated
+ *        by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+refresh_reveal_traits (void *cls,
+                       void **ret,
+                       const char *trait,
+                       unsigned int index)
+{
+  struct RefreshRevealState *rrs = cls;
+  unsigned int num_coins = rrs->num_fresh_coins;
+  #define NUM_TRAITS (num_coins * 3) + 3
+  struct TALER_TESTING_Trait traits[NUM_TRAITS];
+  unsigned int i;
+
+  /* Making coin privs traits */
+  for (i=0; i<num_coins; i++)
+    traits[i] = TALER_TESTING_make_trait_coin_priv
+      (i, &rrs->fresh_coins[i].coin_priv);  
+
+  /* Making denom pubs traits */
+  for (i=0; i<num_coins; i++)
+    traits[num_coins + i]
+      = TALER_TESTING_make_trait_denom_pub
+        (i, rrs->fresh_coins[i].pk);
+
+  /* Making denom sigs traits */
+  for (i=0; i<num_coins; i++)
+    traits[(num_coins * 2) + i]
+      = TALER_TESTING_make_trait_denom_sig
+        (i, &rrs->fresh_coins[i].sig);
+
+  /* number of fresh coins */
+  traits[(num_coins * 3)] = TALER_TESTING_make_trait_uint
+    (0, &rrs->num_fresh_coins);
+
+  /* whole array of fresh coins */
+  traits[(num_coins * 3) + 1]
+    = TALER_TESTING_make_trait_fresh_coins (0, rrs->fresh_coins),
+
+  /* end of traits */
+  traits[(num_coins * 3) + 2] = TALER_TESTING_trait_end ();
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}
+
+/**
+ * Create a "refresh reveal" command.
+ *
+ * @param label command label
+ * @param exchange connection to the exchange
+ * @param melt_reference reference to a "refresh melt" command
+ * @param expected_response_code expected HTTP response code
+ *
+ * @return the "refresh reveal" command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_refresh_reveal
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *melt_reference,
+   unsigned int expected_response_code)
+{
+  struct RefreshRevealState *rrs;
+  struct TALER_TESTING_Command cmd;
+  
+  rrs = GNUNET_new (struct RefreshRevealState);
+  rrs->melt_reference = melt_reference;
+  rrs->exchange = exchange;
+  rrs->expected_response_code = expected_response_code;
+
+  cmd.cls = rrs;
+  cmd.label = label;
+  cmd.run = &refresh_reveal_run;
+  cmd.cleanup = &refresh_reveal_cleanup;
+  cmd.traits = &refresh_reveal_traits;
+  
+  return cmd;
+}
+
+
+/**
+ * Create a "refresh link" command.
+ *
+ * @param label command label
+ * @param exchange connection to the exchange
+ * @param melt_reference reference to a "refresh melt" command
+ * @param expected_response_code expected HTTP response code
+ *
+ * @return the "refresh link" command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_refresh_link
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *reveal_reference,
+   unsigned int expected_response_code)
+{
+  struct RefreshLinkState *rrs;
+  struct TALER_TESTING_Command cmd;
+  
+  rrs = GNUNET_new (struct RefreshLinkState);
+  rrs->reveal_reference = reveal_reference;
+  rrs->exchange = exchange;
+  rrs->expected_response_code = expected_response_code;
+
+  cmd.cls = rrs;
+  cmd.label = label;
+  cmd.run = &refresh_link_run;
+  cmd.cleanup = &refresh_link_cleanup;
+  
+  return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_refund.c 
b/src/exchange-lib/testing_api_cmd_refund.c
new file mode 100644
index 0000000..a092ee2
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_refund.c
@@ -0,0 +1,295 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 3, or
+  (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange/testing_api_cmd_refund.c
+ * @brief Implement the /refund test command, plus other
+ *        corollary commands (?).
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct RefundState
+{
+  /**
+   * Expected HTTP response code.
+   */
+  unsigned int expected_response_code;
+
+  /**
+   * Amount to be refunded.
+   */
+  const char *refund_amount;
+
+  /**
+   * Expected refund fee.
+   */
+  const char *refund_fee;
+
+  /**
+   * Reference to any command that can provide a coin to refund.
+   */
+  const char *coin_reference;
+
+  /**
+   * Refund transaction identifier.  Left un-initialized in the
+   * old test-suite.  What's the best way to init it?
+   */
+  uint64_t refund_transaction_id;
+
+  /**
+   * Connection to the exchange.
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * Handle to the refund operation.
+   */
+  struct TALER_EXCHANGE_RefundHandle *rh;
+
+  /**
+   * Interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *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 RefundState *rs = cls;
+  struct TALER_TESTING_Command *refund_cmd;
+
+  refund_cmd = &rs->is->commands[rs->is->ip];
+  rs->rh = NULL;
+  if (rs->expected_response_code != http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u to command %s\n",
+                http_status,
+                refund_cmd->label);
+    json_dumpf (obj, stderr, 0);
+    TALER_TESTING_interpreter_fail (rs->is);
+    return;
+  }
+
+  switch (http_status)
+  {
+  case MHD_HTTP_OK:
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Good /refund status code\n");
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Unmanaged HTTP status code: %u, command: %s\n",
+                http_status, refund_cmd->label);
+  }
+
+  TALER_TESTING_interpreter_next (rs->is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param i the interpreter state.
+ */
+void
+refund_run (void *cls,
+            const struct TALER_TESTING_Command *cmd,
+            struct TALER_TESTING_Interpreter *is)
+{
+  struct RefundState *rs = cls; 
+  struct TALER_CoinSpendPrivateKeyP *coin_priv;
+  struct TALER_CoinSpendPublicKeyP coin;
+  const char *contract_terms;
+  struct GNUNET_HashCode h_contract_terms;
+  json_t *j_contract_terms;
+  struct TALER_Amount refund_fee;
+  struct TALER_Amount refund_amount;
+  const struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv;
+  const struct TALER_TESTING_Command *coin_cmd;
+
+  rs->exchange = is->exchange;
+  rs->is = is;
+
+  if (GNUNET_OK !=
+      TALER_string_to_amount (rs->refund_amount,
+                              &refund_amount))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to parse amount `%s' at %u/%s\n",
+                rs->refund_amount,
+                is->ip,
+                cmd->label);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  if (GNUNET_OK !=
+      TALER_string_to_amount (rs->refund_fee,
+                              &refund_fee))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to parse amount `%s' at %u/%s\n",
+                rs->refund_fee,
+                is->ip,
+                cmd->label);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+
+  coin_cmd = TALER_TESTING_interpreter_lookup_command
+    (is, rs->coin_reference);
+
+  if (NULL == coin_cmd)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+
+  if (GNUNET_OK != TALER_TESTING_get_trait_contract_terms
+    (coin_cmd, 0, &contract_terms))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return; 
+  }
+
+  j_contract_terms = json_loads
+    (contract_terms, JSON_REJECT_DUPLICATES, NULL);
+
+  /* Very unlikely to fail */
+  GNUNET_assert (NULL != j_contract_terms);
+  GNUNET_assert (GNUNET_OK == TALER_JSON_hash
+    (j_contract_terms, &h_contract_terms));
+
+  json_decref (j_contract_terms);
+
+  /* Hunting for a coin .. */
+  if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
+    (coin_cmd, 0, &coin_priv))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+
+  GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
+                                      &coin.eddsa_pub);
+  if (GNUNET_OK != TALER_TESTING_get_trait_peer_key
+    (coin_cmd, 0, &merchant_priv))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return; 
+  }
+
+  rs->rh = TALER_EXCHANGE_refund
+    (rs->exchange, &refund_amount, &refund_fee, &h_contract_terms,
+     &coin, rs->refund_transaction_id,
+     (const struct TALER_MerchantPrivateKeyP *) merchant_priv,
+     &refund_cb, rs);
+
+  GNUNET_assert (NULL != rs->rh);
+}
+
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+refund_cleanup (void *cls,
+                const struct TALER_TESTING_Command *cmd)
+{
+  struct RefundState *rs = cls;
+
+  if (NULL != rs->rh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Command %u (%s) did not complete\n",
+                rs->is->ip,
+                cmd->label);
+    TALER_EXCHANGE_refund_cancel (rs->rh);
+    rs->rh = NULL;
+  }
+  GNUNET_free (rs);
+}
+
+/**
+ * Create a /refund test command.
+ *
+ * @param label command label
+ * @param expected_response_code expected HTTP status code
+ * @param refund_amount the amount to ask a refund for
+ * @param refund_fee expected refund fee
+ * @param coin_reference reference to a command that can
+ *        provide a coin to be refunded.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_refund (const char *label,
+                          unsigned int expected_response_code,
+                          const char *refund_amount,
+                          const char *refund_fee,
+                          const char *coin_reference)
+{
+  struct RefundState *rs;
+  struct TALER_TESTING_Command cmd;
+
+  rs = GNUNET_new (struct RefundState);
+
+  rs->expected_response_code = expected_response_code;
+  rs->refund_amount = refund_amount;
+  rs->refund_fee = refund_fee;
+  rs->coin_reference = coin_reference;
+
+  cmd.cls = rs;
+  cmd.label = label;
+  cmd.run = &refund_run;
+  cmd.cleanup = &refund_cleanup;
+
+  return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_signal.c 
b/src/exchange-lib/testing_api_cmd_signal.c
new file mode 100644
index 0000000..5b0fa1b
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_signal.c
@@ -0,0 +1,112 @@
+/*
+  This file is part of TALER
+  (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 3, or
+  (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_signal.c
+ * @brief command(s) to send signals to processes.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct SignalState
+{
+  /**
+   * The process to send the signal to.
+   */
+  struct GNUNET_OS_Process *process;
+
+  /**
+   * The signal to send to the process.
+   */
+  int signal;
+
+};
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct SignalState.
+ * @param cmd the command to execute, a /wire one.
+ * @param is the interpreter state.
+ */
+void
+signal_run (void *cls,
+            const struct TALER_TESTING_Command *cmd,
+            struct TALER_TESTING_Interpreter *is)
+{
+  struct SignalState *ss = cls;
+
+  GNUNET_break (0 == GNUNET_OS_process_kill
+    (ss->process, ss->signal));
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Signaling '%d'..\n",
+              ss->signal);
+  sleep (6);
+  TALER_TESTING_interpreter_next (is);
+}
+
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct SignalState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+signal_cleanup (void *cls,
+                const struct TALER_TESTING_Command *cmd)
+{
+  struct SignalState *ss = cls;
+
+  GNUNET_free (ss);
+}
+
+/**
+ * Send a signal to a process.
+ *
+ * @param process handle to the process
+ * @param signal signal to send
+ *
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_signal (const char *label,
+                          struct GNUNET_OS_Process *process,
+                          int signal)
+{
+  struct SignalState *ss;
+  struct TALER_TESTING_Command cmd;
+
+  ss = GNUNET_new (struct SignalState);
+
+  ss->process = process;
+  ss->signal = signal;
+
+  cmd.cls = ss;
+  cmd.label = label;
+  cmd.run = &signal_run;
+  cmd.cleanup = &signal_cleanup;
+
+  return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_status.c 
b/src/exchange-lib/testing_api_cmd_status.c
new file mode 100644
index 0000000..c948277
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_status.c
@@ -0,0 +1,240 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 3, or
+  (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange/testing_api_cmd_status.c
+ * @brief Implement the /reserve/status test command.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct StatusState
+{
+  /**
+   * Label to the command which created the reserve to check,
+   * needed to resort the reserve key.
+   */
+   const char *reserve_reference;
+
+  /**
+   * Handle to a /reserve/status operation.
+   */
+  struct TALER_EXCHANGE_ReserveStatusHandle *rsh;
+
+  /**
+   * Expected reserve balance.
+   */
+  const char *expected_balance;
+
+  /**
+   * Expected HTTP response code.
+   */
+  unsigned int expected_response_code;
+
+  /**
+   * Handle to the exchange.
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * Interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+};
+
+/**
+ * Check exchange returned expected values.
+ *
+ * @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[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
+ */
+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 StatusState *ss = cls;
+  struct TALER_Amount eb;
+  
+  ss->rsh = NULL;
+  if (ss->expected_response_code != http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected HTTP response code: %d\n",
+                http_status);
+    TALER_TESTING_interpreter_fail (ss->is);
+    return;
+  }
+
+  GNUNET_assert (GNUNET_OK == TALER_string_to_amount
+    (ss->expected_balance, &eb));
+
+  if (0 != TALER_amount_cmp (&eb, balance))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected amount in reserve\n");
+    TALER_TESTING_interpreter_fail (ss->is);
+    return;
+  }
+
+/**
+ * Fixme: need a way to check if reserve history is consistent.
+ * Every command which relates to reserve 'x' should be added in
+ * a linked list of all commands that relate to the same reserve
+ * 'x'.
+ *
+ * API-wise, any command that relates to a reserve should offer a
+ * method called e.g. "compare_with_history" that takes an element
+ * of the array returned by "/reserve/status" and checks if that
+ * element correspond to itself (= the command exposing the check-
+ * method).
+ */
+
+  TALER_TESTING_interpreter_next (ss->is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param i the interpreter state.
+ */
+void
+status_run (void *cls,
+            const struct TALER_TESTING_Command *cmd,
+            struct TALER_TESTING_Interpreter *is)
+{
+
+  struct StatusState *ss = cls;
+  const struct TALER_TESTING_Command *create_reserve;
+  struct TALER_ReservePrivateKeyP *reserve_priv;
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+  ss->is = is;
+  GNUNET_assert (NULL != ss->reserve_reference);
+
+  create_reserve
+    = TALER_TESTING_interpreter_lookup_command
+      (is, ss->reserve_reference);
+
+  if (NULL == create_reserve)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+
+  if (GNUNET_OK !=
+      TALER_TESTING_get_trait_reserve_priv (create_reserve,
+                                            0,
+                                            &reserve_priv))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+
+  GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, 
+                                      &reserve_pub.eddsa_pub);
+  ss->rsh
+    = TALER_EXCHANGE_reserve_status (ss->exchange,
+                                     &reserve_pub,
+                                     &reserve_status_cb,
+                                     ss);
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+status_cleanup (void *cls,
+                const struct TALER_TESTING_Command *cmd)
+{
+  struct StatusState *ss = cls;
+
+  if (NULL != ss->rsh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Command %u (%s) did not complete\n",
+                ss->is->ip,
+                cmd->label);
+    TALER_EXCHANGE_reserve_status_cancel (ss->rsh);
+    ss->rsh = NULL;
+  }
+  GNUNET_free (ss);
+}
+
+
+/**
+ * Create a /reserve/status command.
+ *
+ * @param label the command label.
+ * @param exchange the exchange to connect to.
+ * @param reserve_reference reference to the reserve to check.
+ * @param expected_balance balance expected to be at the referenced reserve.
+ * @param expected_response_code expected HTTP response code.
+ *
+ * @return the command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_status (const char *label,
+                          struct TALER_EXCHANGE_Handle *exchange,
+                          const char *reserve_reference,
+                          const char *expected_balance,
+                          unsigned int expected_response_code)
+{
+  struct TALER_TESTING_Command cmd;
+  struct StatusState *ss;
+
+  ss = GNUNET_new (struct StatusState);
+  ss->exchange = exchange;
+  ss->reserve_reference = reserve_reference;
+  ss->expected_balance = expected_balance;
+  ss->expected_response_code = expected_response_code;
+
+  cmd.cls = ss;
+  cmd.label = label;
+  cmd.run = &status_run;
+  cmd.cleanup = &status_cleanup;
+
+  return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_track.c 
b/src/exchange-lib/testing_api_cmd_track.c
new file mode 100644
index 0000000..4089487
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_track.c
@@ -0,0 +1,819 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 3, or
+  (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange/testing_api_cmd_status.c
+ * @brief Implement the /reserve/status test command.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct TrackTransactionState
+{
+
+  /**
+   * Which #OC_CHECK_BANK_TRANSFER wtid should this match? NULL
+   * for none.
+   */
+  const char *bank_transfer_reference;
+
+  /**
+   * Wire transfer identifier, set if #MHD_HTTP_OK was the
+   * response code.
+   */
+  struct TALER_WireTransferIdentifierRawP wtid;
+
+  /**
+   * Expected HTTP response code.
+   */
+  unsigned int expected_response_code;
+
+  /**
+   * Reference to any operation that can provide a transaction.
+   * Tipically a /deposit operation.
+   */
+  const char *transaction_reference;
+
+  /**
+   * Index of the coin involved in the transaction.
+   */
+  unsigned int coin_index;
+
+  /**
+   * Handle to the deposit wtid request.
+   */
+  struct TALER_EXCHANGE_TrackTransactionHandle *tth;
+
+  /**
+   * Handle to the exchange.
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * Interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+};
+
+struct TrackTransferState
+{
+
+  /**
+   * Expected amount for this WTID.
+   */
+  const char *expected_total_amount;
+
+  /**
+   * Expected fee for this WTID.
+   */
+  const char *expected_wire_fee;
+
+  /**
+   * Expected HTTP response code.
+   */
+  unsigned int expected_response_code;
+
+  /**
+   * Reference to any operation that can provide a WTID.
+   */
+  const char *wtid_reference;
+
+  /**
+   * Reference to any operation that can provide wire details.
+   */
+  const char *wire_details_reference;
+
+  /**
+   * Reference to any operation that can provide an amount.
+   */
+  const char *total_amount_reference;
+
+  /**
+   * Index to the WTID to pick.
+   */
+  unsigned int index;
+
+  /**
+   * Handle to the deposit wtid request.
+   */
+  struct TALER_EXCHANGE_TrackTransferHandle *tth;
+
+  /**
+   * Handle to the exchange.
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * Interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *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 TrackTransactionState *tts = cls;
+  struct TALER_TESTING_Interpreter *is = tts->is;
+  struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
+
+  tts->tth = NULL;
+  if (tts->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);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  switch (http_status)
+  {
+  case MHD_HTTP_OK:
+    tts->wtid = *wtid;
+    if (NULL != tts->bank_transfer_reference)
+    {
+      const struct TALER_TESTING_Command *bank_transfer_cmd; 
+      char *ws;
+
+      ws = GNUNET_STRINGS_data_to_string_alloc (wtid,
+                                                sizeof (*wtid));
+      bank_transfer_cmd = TALER_TESTING_interpreter_lookup_command
+        (is, tts->bank_transfer_reference);
+
+      if (NULL == bank_transfer_cmd)
+      {
+        GNUNET_break (0);
+        TALER_TESTING_interpreter_fail (is);
+        return;
+      }
+
+      char *transfer_subject;
+
+      if (GNUNET_OK != TALER_TESTING_get_trait_transfer_subject
+        (bank_transfer_cmd, 0, &transfer_subject))
+      {
+        GNUNET_break (0);
+        TALER_TESTING_interpreter_fail (is);
+        return;       
+      }
+
+      if (0 != strcmp (ws, transfer_subject))
+      {
+        GNUNET_break (0);
+        GNUNET_free (ws);
+        TALER_TESTING_interpreter_fail (tts->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;
+  }
+  TALER_TESTING_interpreter_next (tts->is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /track/transaction one.
+ * @param is the interpreter state.
+ */
+void
+track_transaction_run (void *cls,
+                       const struct TALER_TESTING_Command *cmd,
+                       struct TALER_TESTING_Interpreter *is)
+{
+  struct TrackTransactionState *tts = cls;
+  const struct TALER_TESTING_Command *transaction_cmd;
+  struct TALER_CoinSpendPrivateKeyP *coin_priv;
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+  const char *wire_details;
+  const char *contract_terms;
+  json_t *j_wire_details;
+  json_t *j_contract_terms;
+  struct GNUNET_HashCode h_wire_details;
+  struct GNUNET_HashCode h_contract_terms;
+  const struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv;
+  
+  tts->is = is;
+  transaction_cmd = TALER_TESTING_interpreter_lookup_command
+    (tts->is, tts->transaction_reference);
+
+  if (NULL == transaction_cmd)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (tts->is);
+    return;  
+  }
+
+  if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
+    (transaction_cmd, tts->coin_index, &coin_priv))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (tts->is);
+    return;  
+  }
+
+  GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
+                                      &coin_pub.eddsa_pub);
+
+  /* Get the strings.. */
+  if (GNUNET_OK != TALER_TESTING_get_trait_wire_details
+    (transaction_cmd, 0, &wire_details))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (tts->is);
+    return;
+  }
+
+  if (GNUNET_OK != TALER_TESTING_get_trait_contract_terms
+    (transaction_cmd, 0, &contract_terms))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (tts->is);
+    return;
+  }
+
+  /* Parse them.. */
+  j_wire_details = json_loads
+    (wire_details, JSON_REJECT_DUPLICATES, NULL);
+  j_contract_terms = json_loads
+    (contract_terms, JSON_REJECT_DUPLICATES, NULL);
+  
+  if ((NULL == j_wire_details) || (NULL == j_contract_terms))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (tts->is);
+    return;  
+  }
+
+  /* Should not fail here, json has been parsed already */
+  GNUNET_assert
+    ( (GNUNET_OK == TALER_JSON_hash (j_wire_details,
+                                      &h_wire_details)) &&
+      (GNUNET_OK == TALER_JSON_hash (j_contract_terms,
+                                      &h_contract_terms)) );
+
+  if (GNUNET_OK != TALER_TESTING_get_trait_peer_key
+    (transaction_cmd, 0, &merchant_priv))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (tts->is);
+    return; 
+  }
+
+  tts->tth = TALER_EXCHANGE_track_transaction
+    (tts->exchange,
+     (struct TALER_MerchantPrivateKeyP *) merchant_priv,
+     &h_wire_details,
+     &h_contract_terms,
+     &coin_pub,
+     &deposit_wtid_cb,
+     tts);
+
+  GNUNET_assert (NULL != tts->tth);
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+track_transaction_cleanup
+  (void *cls,
+   const struct TALER_TESTING_Command *cmd)
+{
+  struct TrackTransactionState *tts = cls;
+
+  if (NULL != tts->tth)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Command %u (%s) did not complete\n",
+                tts->is->ip,
+                cmd->label);
+    TALER_EXCHANGE_track_transaction_cancel (tts->tth);
+    tts->tth = NULL;
+  }
+  GNUNET_free (tts);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+track_transaction_traits (void *cls,
+                          void **ret,
+                          const char *trait,
+                          unsigned int index)
+{
+  struct TrackTransactionState *tts = cls;
+
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_wtid (0, &tts->wtid),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}
+
+/**
+ * Create a /track/transaction command.
+ *
+ * @param label the command label.
+ * @param exchange the exchange to connect to.
+ * @param transaction_reference reference to a deposit operation.
+ * @param coin_index index of the coin involved in the transaction
+ * @param expected_response_code expected HTTP response code.
+ * @param bank_transfer_reference which #OC_CHECK_BANK_TRANSFER
+ *        wtid should this match? NULL
+   * for none
+ *
+ * @return the command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_track_transaction
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *transaction_reference,
+   unsigned int coin_index,
+   unsigned int expected_response_code,
+   const char *bank_transfer_reference)
+{
+  struct TALER_TESTING_Command cmd;
+  struct TrackTransactionState *tts;
+
+  tts = GNUNET_new (struct TrackTransactionState);
+  tts->exchange = exchange;
+  tts->transaction_reference = transaction_reference;
+  tts->expected_response_code = expected_response_code;
+  tts->bank_transfer_reference = bank_transfer_reference;
+  tts->coin_index = coin_index;
+
+  cmd.cls = tts;
+  cmd.label = label;
+  cmd.run = &track_transaction_run;
+  cmd.cleanup = &track_transaction_cleanup;
+  cmd.traits = &track_transaction_traits;
+
+  return cmd;
+
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+track_transfer_cleanup (void *cls,
+                        const struct TALER_TESTING_Command *cmd)
+{
+
+  struct TrackTransferState *tts = cls;
+
+  if (NULL != tts->tth)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Command %u (%s) did not complete\n",
+                tts->is->ip,
+                cmd->label);
+    TALER_EXCHANGE_track_transfer_cancel (tts->tth);
+    tts->tth = NULL;
+  }
+  GNUNET_free (tts);
+
+}
+
+/**
+ * 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
+track_transfer_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 TrackTransferState *tts = cls;
+  struct TALER_TESTING_Interpreter *is = tts->is;
+  struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
+
+  const struct TALER_TESTING_Command *wtid_cmd;
+  struct TALER_Amount expected_amount;
+
+  tts->tth = NULL;
+
+  if (tts->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);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+
+  switch (http_status)
+  {
+
+  if (
+    (NULL == tts->expected_total_amount) ||
+      (NULL == tts->expected_wire_fee))
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Expected amount and fee not specified, "
+                "likely to segfault...\n");
+
+  case MHD_HTTP_OK:
+    if (GNUNET_OK !=
+        TALER_string_to_amount (tts->expected_total_amount,
+                                &expected_amount))
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (is);
+      return;
+    }
+    if (0 != TALER_amount_cmp (total_amount,
+                               &expected_amount))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Total amount missmatch to command %s - "
+                  "%s vs %s\n",
+                  cmd->label,
+                  TALER_amount_to_string (total_amount),
+                  TALER_amount_to_string (&expected_amount));
+      json_dumpf (json, stderr, 0);
+      fprintf (stderr, "\n");
+      TALER_TESTING_interpreter_fail (is);
+      return;
+    }
+
+    if (GNUNET_OK !=
+        TALER_string_to_amount (tts->expected_wire_fee,
+                                &expected_amount))
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_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);
+      TALER_TESTING_interpreter_fail (is);
+      return;
+    }
+
+    wtid_cmd = TALER_TESTING_interpreter_lookup_command
+      (is, tts->wtid_reference);
+
+    if (NULL == wtid_cmd)
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (is);
+      return;    
+    }
+
+    /**
+     * Optionally checking: (1) wire-details for this transfer
+     * match the ones from a referenced "deposit" operation -
+     * or any operation that could provide wire-details.  (2)
+     * Total amount for this transfer matches the one from any
+     * referenced command that could provide one.
+     */
+
+    if (NULL != tts->wire_details_reference)
+    {
+      const struct TALER_TESTING_Command *wire_details_cmd;
+      const char *wire_details;
+      json_t *j_wire_details;
+      struct GNUNET_HashCode h_wire_details;
+
+      if (NULL == (wire_details_cmd
+        = TALER_TESTING_interpreter_lookup_command
+          (is, tts->wire_details_reference)))
+      {
+        GNUNET_break (0);
+        TALER_TESTING_interpreter_fail (is);
+        return;
+      }
+
+      if (GNUNET_OK != TALER_TESTING_get_trait_wire_details
+        (wire_details_cmd, 0, &wire_details))
+      {
+        GNUNET_break (0);
+        TALER_TESTING_interpreter_fail (is);
+        return;
+      }
+
+      j_wire_details = json_loads
+        (wire_details, JSON_REJECT_DUPLICATES, NULL);
+
+      GNUNET_assert (NULL != j_wire_details);
+
+      GNUNET_assert (GNUNET_OK == TALER_JSON_hash
+        (j_wire_details, &h_wire_details));
+
+      if (0 != memcmp (&h_wire_details,
+                       h_wire,
+                       sizeof (struct GNUNET_HashCode)))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Wire hash missmath to command %s\n",
+                    cmd->label);
+        json_dumpf (json, stderr, 0);
+        TALER_TESTING_interpreter_fail (is);
+        return;
+      }
+    }
+    if (NULL != tts->total_amount_reference)
+    {
+      const struct TALER_TESTING_Command *total_amount_cmd;
+      const char *total_amount_from_reference_str;
+      struct TALER_Amount total_amount_from_reference;
+
+      if (NULL == (total_amount_cmd
+        = TALER_TESTING_interpreter_lookup_command
+          (is, tts->total_amount_reference)))
+      {
+        GNUNET_break (0);
+        TALER_TESTING_interpreter_fail (is);
+        return;      
+      }
+
+      if (GNUNET_OK != TALER_TESTING_get_trait_amount
+        (total_amount_cmd, 0, &total_amount_from_reference_str))
+      {
+        GNUNET_break (0);
+        TALER_TESTING_interpreter_fail (is);
+        return;
+      }
+      
+      GNUNET_assert (GNUNET_OK == TALER_string_to_amount
+        (total_amount_from_reference_str,
+         &total_amount_from_reference));
+
+      if (0 != TALER_amount_cmp (total_amount,
+                                 &total_amount_from_reference))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Amount missmath to command %s\n",
+                    cmd->label);
+        json_dumpf (json, stderr, 0);
+        TALER_TESTING_interpreter_fail (is);
+        return;
+      }
+    }
+  }
+  TALER_TESTING_interpreter_next (is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure.
+ * @param cmd the command to execute, a /track/transfer one.
+ * @param is the interpreter state.
+ */
+void
+track_transfer_run (void *cls,
+                    const struct TALER_TESTING_Command *cmd,
+                    struct TALER_TESTING_Interpreter *is)
+{
+  /* looking for a wtid to track .. */
+
+  struct TrackTransferState *tts = cls;
+  struct TALER_WireTransferIdentifierRawP wtid;
+  struct TALER_WireTransferIdentifierRawP *wtid_ptr;
+
+  /* If no reference is given, we'll use a all-zeros
+   * WTID */
+  memset (&wtid, 0, sizeof (wtid));
+  wtid_ptr = &wtid;
+  
+  tts->is = is;
+  if (NULL != tts->wtid_reference)
+  {
+    const struct TALER_TESTING_Command *wtid_cmd;
+
+    wtid_cmd = TALER_TESTING_interpreter_lookup_command
+      (tts->is, tts->wtid_reference);
+
+    if (NULL == wtid_cmd)
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (tts->is);
+      return;
+    }
+
+    if (GNUNET_OK != TALER_TESTING_get_trait_wtid
+      (wtid_cmd, tts->index, &wtid_ptr))
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (tts->is);
+      return;     
+    }
+  }
+  tts->tth = TALER_EXCHANGE_track_transfer (tts->exchange,
+                                            wtid_ptr,
+                                            &track_transfer_cb,
+                                            tts);
+
+  GNUNET_assert (NULL != tts->tth);
+}
+
+/**
+ * Make a /track/transfer command, expecting the transfer
+ * not being done (yet).
+ *
+ * @param label the command label
+ * @param exchange connection to the exchange
+ * @param wtid_reference reference to any command which can provide
+ *        a wtid
+ * @param index in case there are multiple wtid offered, this
+ *        parameter selects a particular one
+ * @param expected_response_code expected HTTP response code
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_track_transfer_empty
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *wtid_reference,
+   unsigned int index,
+   unsigned int expected_response_code)
+{
+  struct TrackTransferState *tts;
+  struct TALER_TESTING_Command cmd;
+  
+  tts = GNUNET_new (struct TrackTransferState);
+
+  tts->wtid_reference = wtid_reference;
+  tts->index = index;
+  tts->expected_response_code = expected_response_code;
+  tts->exchange = exchange;
+
+  cmd.cls = tts;
+  cmd.label = label;
+  cmd.run = &track_transfer_run;
+  cmd.cleanup = &track_transfer_cleanup;
+
+  return cmd; 
+}
+
+/**
+ * Make a /track/transfer command, specifying which amount and
+ * wire fee are expected.
+ *
+ * @param label the command label
+ * @param exchange connection to the exchange
+ * @param wtid_reference reference to any command which can provide
+ *        a wtid
+ * @param index in case there are multiple wtid offered, this
+ *        parameter selects a particular one
+ * @param expected_response_code expected HTTP response code
+ * @param expected_amount how much money we expect being
+ *        moved with this wire-transfer.
+ * @param expected_wire_fee expected wire fee.
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_track_transfer
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *wtid_reference,
+   unsigned int index,
+   unsigned int expected_response_code,
+   const char *expected_total_amount,
+   const char *expected_wire_fee)
+{
+  struct TrackTransferState *tts;
+  struct TALER_TESTING_Command cmd;
+  
+  tts = GNUNET_new (struct TrackTransferState);
+
+  tts->wtid_reference = wtid_reference;
+  tts->index = index;
+  tts->expected_response_code = expected_response_code;
+  tts->exchange = exchange;
+  tts->expected_total_amount = expected_total_amount;
+  tts->expected_wire_fee = expected_wire_fee;
+
+  cmd.cls = tts;
+  cmd.label = label;
+  cmd.run = &track_transfer_run;
+  cmd.cleanup = &track_transfer_cleanup;
+
+  return cmd; 
+}
diff --git a/src/exchange-lib/testing_api_cmd_wire.c 
b/src/exchange-lib/testing_api_cmd_wire.c
new file mode 100644
index 0000000..f65daec
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_wire.c
@@ -0,0 +1,267 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_wire.c
+ * @brief command for testing /wire.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct WireState
+{
+
+  /**
+   * Handle to the /wire operation.
+   */
+  struct TALER_EXCHANGE_WireHandle *wh;
+
+  /**
+   * Which wire-method we expect are offered by the exchange.
+   */
+  const char *expected_method;
+
+  /**
+   * Flag indicating if the expected method is actually
+   * offered.
+   */
+  unsigned int method_found;
+
+  /**
+   * Fee we expect is charged for this wire-transfer method.
+   */
+  const char *expected_fee;
+
+  /**
+   * Expected HTTP response code.
+   */
+  unsigned int expected_response_code;
+
+  /**
+   * Interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+
+  /**
+   * Connection to the exchange.
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+};
+
+
+/**
+ * Check all the expected values have been returned by /wire.
+ *
+ * @param cls closure
+ * @param wire_method name of the wire method (i.e. "sepa")
+ * @param fees fee structure for this method
+ */
+static void
+check_method_and_fee_cb
+  (void *cls,
+   const char *wire_method,
+   const struct TALER_EXCHANGE_WireAggregateFees *fees);
+
+/**
+ * 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 obj the received JSON reply, if successful this should
+ *            be the wire format details as provided by /wire.
+ */
+static void
+wire_cb (void *cls,
+         unsigned int http_status,
+        enum TALER_ErrorCode ec,
+         const json_t *obj)
+{
+  struct WireState *ws = cls;
+  struct TALER_TESTING_Command *cmd
+    = &ws->is->commands[ws->is->ip];
+
+  /**
+   * The handle has been free'd by GNUnet curl-lib. FIXME:
+   * shouldn't GNUnet nullify it once it frees it?
+   */
+  ws->wh = NULL;
+  if (ws->expected_response_code != http_status)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (ws->is);
+    return;
+  }
+
+  if (GNUNET_OK != TALER_EXCHANGE_wire_get_fees
+       (&TALER_EXCHANGE_get_keys (ws->exchange)->master_pub,
+        obj,
+        // will check synchronously.
+        &check_method_and_fee_cb,
+        ws))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Wire fee extraction in command %s failed\n",
+                cmd->label);
+    json_dumpf (obj, stderr, 0);
+    TALER_TESTING_interpreter_fail (ws->is);
+    return;
+  }
+
+  if (ws->method_found != GNUNET_OK)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "/wire does not offer method '%s'\n",
+                ws->expected_method);
+    TALER_TESTING_interpreter_fail (ws->is);
+    return;
+  }
+  TALER_TESTING_interpreter_next (ws->is);
+}
+
+/**
+ * Check all the expected values have been returned by /wire.
+ *
+ * @param cls closure
+ * @param wire_method name of the wire method (i.e. "sepa")
+ * @param fees fee structure for this method
+ */
+static void
+check_method_and_fee_cb
+  (void *cls,
+   const char *wire_method,
+   const struct TALER_EXCHANGE_WireAggregateFees *fees)
+{
+  struct WireState *ws = cls;
+  struct TALER_TESTING_Command *cmd
+    = &ws->is->commands[ws->is->ip]; // ugly?
+  struct TALER_Amount expected_fee;
+
+  if (0 == strcmp (ws->expected_method, wire_method))
+    ws->method_found = GNUNET_OK;
+
+  if ( ws->expected_fee && (ws->method_found == GNUNET_OK) )
+  {
+    GNUNET_assert (GNUNET_OK == TALER_string_to_amount
+                     (ws->expected_fee,
+                      &expected_fee));
+    while (NULL != fees)
+    {
+      if (0 != TALER_amount_cmp (&fees->wire_fee,
+                                 &expected_fee))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Wire fee missmatch to command %s\n",
+                    cmd->label);
+        TALER_TESTING_interpreter_fail (ws->is);
+        return;
+      }
+      fees = fees->next;
+    }
+  }
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param i the interpreter state.
+ */
+void
+wire_run (void *cls,
+          const struct TALER_TESTING_Command *cmd,
+          struct TALER_TESTING_Interpreter *i)
+{
+  struct WireState *ws = cls;
+  ws->is = i;
+  ws->wh = TALER_EXCHANGE_wire (ws->exchange,
+                                &wire_cb,
+                                ws);
+}
+
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+wire_cleanup (void *cls,
+              const struct TALER_TESTING_Command *cmd)
+{
+  struct WireState *ws = cls;
+
+  if (NULL != ws->wh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Command %u (%s) did not complete\n",
+                ws->is->ip,
+                cmd->label);
+    TALER_EXCHANGE_wire_cancel (ws->wh);
+    ws->wh = NULL;
+  }
+  GNUNET_free (ws);
+}
+
+/**
+ * Create a /wire command.
+ *
+ * @param label the command label.
+ * @param exchange the exchange to connect to.
+ * @param expected_method which wire-transfer method is expected
+ *        to be offered by the exchange.
+ * @param expected_fee the fee the exchange should charge.
+ * @param expected_response_code the HTTP response the exchange
+ *        should return.
+ *
+ * @return the command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_wire (const char *label,
+                        struct TALER_EXCHANGE_Handle *exchange,
+                        const char *expected_method,
+                        const char *expected_fee,
+                        unsigned int expected_response_code)
+{
+  struct TALER_TESTING_Command cmd;
+  struct WireState *ws;
+
+  ws = GNUNET_new (struct WireState);
+  ws->exchange = exchange;
+  ws->expected_method = expected_method;
+  ws->expected_fee = expected_fee;
+  ws->expected_response_code = expected_response_code;
+
+  cmd.cls = ws;
+  cmd.label = label;
+  cmd.run = &wire_run;
+  cmd.cleanup = &wire_cleanup;
+
+  return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_withdraw.c 
b/src/exchange-lib/testing_api_cmd_withdraw.c
index 642722b..eb3bc8a 100644
--- a/src/exchange-lib/testing_api_cmd_withdraw.c
+++ b/src/exchange-lib/testing_api_cmd_withdraw.c
@@ -2,16 +2,18 @@
   This file is part of TALER
   Copyright (C) 2018 Taler Systems SA
 
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along with
-  TALER; see the file COPYING.  If not, see
+  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/>
 */
 /**
@@ -85,14 +87,17 @@ struct WithdrawState
 
 
 /**
- * Function called upon completion of our /reserve/withdraw request.
+ * Function called upon completion of our /reserve/withdraw
+ * request.
  *
  * @param cls closure with the withdraw 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 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)
+ * @param full_response full response from the exchange (for
+ *        logging, in case of errors)
  */
 static void
 reserve_withdraw_cb (void *cls,
@@ -164,9 +169,8 @@ withdraw_run (void *cls,
   struct TALER_ReservePrivateKeyP *rp;
   const struct TALER_TESTING_Command *create_reserve;
 
-  create_reserve
-    = TALER_TESTING_interpreter_lookup_command (is,
-                                                ws->reserve_reference);
+  create_reserve = TALER_TESTING_interpreter_lookup_command
+    (is, ws->reserve_reference);
   if (NULL == create_reserve)
   {
     GNUNET_break (0);
@@ -175,7 +179,7 @@ withdraw_run (void *cls,
   }
   if (GNUNET_OK !=
       TALER_TESTING_get_trait_reserve_priv (create_reserve,
-                                            NULL,
+                                            0,
                                             &rp))
   {
     GNUNET_break (0);
@@ -245,38 +249,72 @@ static int
 withdraw_traits (void *cls,
                  void **ret,
                  const char *trait,
-                 const char *selector)
+                 unsigned int index)
 {
   struct WithdrawState *ws = cls;
+  const struct TALER_TESTING_Command *reserve_cmd;
+  struct TALER_ReservePrivateKeyP *reserve_priv;
+
+  /* We offer the reserve key where these coins were withdrawn
+   * from. */
+  reserve_cmd = TALER_TESTING_interpreter_lookup_command
+    (ws->is, ws->reserve_reference);
+
+  if (NULL == reserve_cmd)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (ws->is);
+    return GNUNET_SYSERR;  
+  }
+
+  if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv
+    (reserve_cmd, 0, &reserve_priv))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (ws->is);
+    return GNUNET_SYSERR;  
+  }
+
   struct TALER_TESTING_Trait traits[] = {
-    TALER_TESTING_make_trait_coin_priv (NULL /* only one coin */,
+    TALER_TESTING_make_trait_coin_priv (0 /* only one coin */,
                                         &ws->ps.coin_priv),
-    TALER_TESTING_make_trait_blinding_key (NULL /* only one coin */,
+    TALER_TESTING_make_trait_blinding_key (0 /* only one coin */,
                                            &ws->ps.blinding_key),
-    TALER_TESTING_make_trait_denom_pub (NULL /* only one coin */,
+    TALER_TESTING_make_trait_denom_pub (0 /* only one coin */,
                                         ws->pk),
-    TALER_TESTING_make_trait_denom_sig (NULL /* only one coin */,
+    TALER_TESTING_make_trait_denom_sig (0 /* only one coin */,
                                         &ws->sig),
+    TALER_TESTING_make_trait_reserve_priv (0,
+                                           reserve_priv),
     TALER_TESTING_trait_end ()
   };
 
   return TALER_TESTING_get_trait (traits,
                                   ret,
                                   trait,
-                                  selector);
+                                  index);
 }
 
 
 /**
- * Create withdraw command.
+ * Create a withdraw command.
  *
+ * @param label command label, used by other commands to
+ *        reference this.
+ * @param exchange handle to the exchange.
+ * @param amount how much we withdraw.
+ * @param expected_response_code which HTTP response code
+ *        we expect from the exchange.
+ *
+ * @return the withdraw command to be executed by the interpreter.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_withdraw_amount (const char *label,
-                                   struct TALER_EXCHANGE_Handle *exchange,
-                                   const char *reserve_reference,
-                                   const char *amount,
-                                   unsigned int expected_response_code)
+TALER_TESTING_cmd_withdraw_amount
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *reserve_reference,
+   const char *amount,
+   unsigned int expected_response_code)
 {
   struct TALER_TESTING_Command cmd;
   struct WithdrawState *ws;
@@ -294,8 +332,9 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
                 label);
     GNUNET_assert (0);
   }
-  ws->pk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (exchange),
-                                  &ws->amount);
+  ws->pk = TALER_TESTING_find_pk
+    (TALER_EXCHANGE_get_keys (exchange),
+     &ws->amount);
   if (NULL == ws->pk)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -319,11 +358,12 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
  *
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_withdraw_denomination (const char *label,
-                                         struct TALER_EXCHANGE_Handle 
*exchange,
-                                         const char *reserve_reference,
-                                         const struct 
TALER_EXCHANGE_DenomPublicKey *dk,
-                                         unsigned int expected_response_code)
+TALER_TESTING_cmd_withdraw_denomination
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *reserve_reference,
+   const struct TALER_EXCHANGE_DenomPublicKey *dk,
+   unsigned int expected_response_code)
 {
   struct TALER_TESTING_Command cmd;
   struct WithdrawState *ws;
@@ -349,7 +389,4 @@ TALER_TESTING_cmd_withdraw_denomination (const char *label,
 }
 
 
-
-
-
 /* end of testing_api_cmd_withdraw.c */
diff --git a/src/exchange-lib/testing_api_helpers.c 
b/src/exchange-lib/testing_api_helpers.c
index 1721b3e..a6e421e 100644
--- a/src/exchange-lib/testing_api_helpers.c
+++ b/src/exchange-lib/testing_api_helpers.c
@@ -1,4 +1,3 @@
-
 /*
   This file is part of TALER
   Copyright (C) 2018 Taler Systems SA
@@ -31,6 +30,8 @@
 
 /**
  * Remove files from previous runs
+ *
+ * @param config_name configuration filename.
  */
 void
 TALER_TESTING_cleanup_files (const char *config_name)
@@ -65,7 +66,7 @@ TALER_TESTING_cleanup_files (const char *config_name)
 /**
  * Prepare launching an exchange.  Checks that the configured
  * port is available, runs taler-exchange-keyup,
- * taler-auditor-sign and taler-exchange-dbinit.  Does not
+ * taler-auditor-sign and taler-exchange-dbinit.  Does NOT
  * launch the exchange process itself.
  *
  * @param config_filename configuration file to use
@@ -79,42 +80,16 @@ TALER_TESTING_prepare_exchange (const char *config_filename)
   enum GNUNET_OS_ProcessStatusType type;
   unsigned long code;
   struct GNUNET_CONFIGURATION_Handle *cfg;
-  unsigned long long port;
-
-  cfg = GNUNET_CONFIGURATION_create ();
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_load (cfg,
-                                 config_filename))
-    return GNUNET_NO;
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_number (cfg,
-                                             "exchange",
-                                             "PORT",
-                                             &port))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "exchange",
-                               "PORT");
-    GNUNET_CONFIGURATION_destroy (cfg);
-    return GNUNET_NO;
-  }
-  GNUNET_CONFIGURATION_destroy (cfg);
-  if (GNUNET_OK !=
-      GNUNET_NETWORK_test_port_free (IPPROTO_TCP,
-                                    (uint16_t) port))
-  {
-    fprintf (stderr,
-             "Required port %llu not available, skipping.\n",
-            port);
-    return GNUNET_NO;
-  }
+  char *test_home_dir;
+  char *signed_keys_out;
+  char *exchange_master_pub;
 
   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",
+                                  "-c", config_filename,
                                   "-o", "auditor.in",
                                   NULL);
   if (NULL == proc)
@@ -126,21 +101,58 @@ TALER_TESTING_prepare_exchange (const char 
*config_filename)
   GNUNET_OS_process_wait (proc);
   GNUNET_OS_process_destroy (proc);
 
+  cfg = GNUNET_CONFIGURATION_create ();
+  if (GNUNET_OK != GNUNET_CONFIGURATION_load
+    (cfg, config_filename))
+    return GNUNET_SYSERR;
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             "paths",
+                                             "TALER_TEST_HOME",
+                                             &test_home_dir))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "paths",
+                               "TALER_TEST_HOME");
+    GNUNET_CONFIGURATION_destroy (cfg);
+    return GNUNET_SYSERR;
+  }
+
+  GNUNET_asprintf (&signed_keys_out,
+                   "%s/.local/share/taler/auditors/auditor.out",
+                   test_home_dir);
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             "exchange",
+                                             "MASTER_PUBLIC_KEY",
+                                             &exchange_master_pub))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "exchange",
+                               "MASTER_PUBLIC_KEY");
+    GNUNET_CONFIGURATION_destroy (cfg);
+    return GNUNET_SYSERR;
+  }
+
+  GNUNET_CONFIGURATION_destroy (cfg);
+
   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",
+                                  "-c", config_filename,
                                   "-u", "http://auditor/";,
-                                  "-m", 
"98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG",
+                                  "-m", exchange_master_pub,
                                   "-r", "auditor.in",
-                                  "-o", 
"test_exchange_api_home/.local/share/taler/auditors/auditor.out",
+                                  "-o", signed_keys_out,
                                   NULL);
   if (NULL == proc)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               "Failed to run `taler-exchange-keyup`, is your PATH 
correct?\n");
+               "Failed to run `taler-auditor-sign`, is your PATH correct?\n");
     return GNUNET_NO;
   }
   GNUNET_OS_process_wait (proc);
@@ -151,7 +163,7 @@ TALER_TESTING_prepare_exchange (const char *config_filename)
                                   NULL, NULL, NULL,
                                   "taler-exchange-dbinit",
                                   "taler-exchange-dbinit",
-                                  "-c", "test_exchange_api.conf",
+                                  "-c", config_filename,
                                   "-r",
                                   NULL);
   if (NULL == proc)
@@ -245,22 +257,60 @@ TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys 
*keys,
  * Initialize scheduler loop and curl context for the testcase
  * including starting and stopping the exchange using the given
  * configuration file.
+ *
+ * @param main_cb routine containing all the commands to run.
+ * @param main_cb_cls closure for @a main_cb, typically NULL.
+ * @param config_file configuration file for the test-suite.
+ *
+ * @return FIXME: depends on what TALER_TESTING_setup returns.
  */
 int
 TALER_TESTING_setup_with_exchange (TALER_TESTING_Main main_cb,
                                    void *main_cb_cls,
-                                   const char *config_file)
+                                   const char *config_filename)
 {
   int result;
   unsigned int iter;
   struct GNUNET_OS_Process *exchanged;
 
+  struct GNUNET_CONFIGURATION_Handle *cfg;
+  unsigned long long port;
+
+  cfg = GNUNET_CONFIGURATION_create ();
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_load (cfg,
+                                 config_filename))
+    return GNUNET_NO;
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (cfg,
+                                             "exchange",
+                                             "PORT",
+                                             &port))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "exchange",
+                               "PORT");
+    GNUNET_CONFIGURATION_destroy (cfg);
+    return GNUNET_NO;
+  }
+
+  GNUNET_CONFIGURATION_destroy (cfg);
+  if (GNUNET_OK !=
+      GNUNET_NETWORK_test_port_free (IPPROTO_TCP,
+                                    (uint16_t) port))
+  {
+    fprintf (stderr,
+             "Required port %llu not available, skipping.\n",
+            port);
+    return GNUNET_NO;
+  }
+
   exchanged = GNUNET_OS_start_process (GNUNET_NO,
                                        GNUNET_OS_INHERIT_STD_ALL,
                                        NULL, NULL, NULL,
                                        "taler-exchange-httpd",
                                        "taler-exchange-httpd",
-                                       "-c", config_file,
+                                       "-c", config_filename,
                                        "-i",
                                        NULL);
   /* give child time to start and bind against the socket */
@@ -287,7 +337,9 @@ TALER_TESTING_setup_with_exchange (TALER_TESTING_Main 
main_cb,
   fprintf (stderr, "\n");
 
   result = TALER_TESTING_setup (main_cb,
-                                main_cb_cls);
+                                main_cb_cls,
+                                config_filename,
+                                exchanged);
   GNUNET_break (0 ==
                 GNUNET_OS_process_kill (exchanged,
                                         SIGTERM));
@@ -325,10 +377,32 @@ TALER_TESTING_url_port_free (const char *url)
   return GNUNET_OK;
 }
 
+/**
+ * Allocate and return a piece of wire-details.  Mostly, it adds
+ * the bank_url to the JSON.
+ *
+ * @param template the wire-details template.
+ * @param bank_url the bank_url
+ *
+ * @return the filled out and stringified wire-details.  To
+ *         be manually free'd.
+ */
+char *
+TALER_TESTING_make_wire_details (const char *template,
+                                 const char *bank_url)
+{
+  json_t *jtemplate;
+
+  GNUNET_assert (NULL != (jtemplate = json_loads
+    (template, JSON_REJECT_DUPLICATES, NULL)));
+  GNUNET_assert (0 == json_object_set
+    (jtemplate, "bank_url", json_string (bank_url)));
+  return json_dumps (jtemplate, JSON_COMPACT);
+}
 
 /**
  * Prepare launching a fakebank.  Check that the configuration
- * file has the right option, and that the port is avaiable.
+ * file has the right option, and that the port is available.
  * If everything is OK, return the configured URL of the fakebank.
  *
  * @param config_filename configuration file to use
diff --git a/src/exchange-lib/testing_api_loop.c 
b/src/exchange-lib/testing_api_loop.c
index 8c23649..f22c99d 100644
--- a/src/exchange-lib/testing_api_loop.c
+++ b/src/exchange-lib/testing_api_loop.c
@@ -2,18 +2,21 @@
   This file is part of TALER
   Copyright (C) 2018 Taler Systems SA
 
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along with
-  TALER; see the file COPYING.  If not, see
+  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-lib/testing_api_loop.c
  * @brief main interpreter loop for testcases
@@ -28,73 +31,12 @@
 #include "taler_testing_lib.h"
 #include "taler_fakebank_lib.h"
 
-
-/**
- * Global state of the interpreter, used by a command
- * to access information about other commands.
- */
-struct TALER_TESTING_Interpreter
-{
-
-  /**
-   * Commands the interpreter will run.
-   */
-  struct TALER_TESTING_Command *commands;
-
-  /**
-   * Interpreter task (if one is scheduled).
-   */
-  struct GNUNET_SCHEDULER_Task *task;
-
-  /**
-   * ID of task called whenever we get a SIGCHILD.
-   * Used for #TALER_TESTING_wait_for_sigchld().
-   */
-  struct GNUNET_SCHEDULER_Task *child_death_task;
-
-  /**
-   * Main execution context for the main loop.
-   */
-  struct GNUNET_CURL_Context *ctx;
-
-  /**
-   * Context for running the CURL event loop.
-   */
-  struct GNUNET_CURL_RescheduleContext *rc;
-
-  /**
-   * Handle to our fakebank, if #TALER_TESTING_run_with_fakebank() was used.
-   * Otherwise NULL.
-   */
-  struct TALER_FAKEBANK_Handle *fakebank;
-
-  /**
-   * Task run on timeout.
-   */
-  struct GNUNET_SCHEDULER_Task *timeout_task;
-
-  /**
-   * Instruction pointer.  Tells #interpreter_run() which
-   * instruction to run next.
-   */
-  unsigned int ip;
-
-  /**
-   * Result of the testcases, #GNUNET_OK on success
-   */
-  int result;
-
-};
-
-
 /**
  * Pipe used to communicate child death via signal.
  * Must be global, as used in signal handler!
  */
 static struct GNUNET_DISK_PipeHandle *sigpipe;
 
-
-
 /**
  * Lookup command by label.
  *
@@ -103,8 +45,9 @@ static struct GNUNET_DISK_PipeHandle *sigpipe;
  * @return NULL if command was not found
  */
 const struct TALER_TESTING_Command *
-TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is,
-                                          const char *label)
+TALER_TESTING_interpreter_lookup_command
+  (struct TALER_TESTING_Interpreter *is,
+   const char *label)
 {
   const struct TALER_TESTING_Command *cmd;
 
@@ -131,23 +74,35 @@ TALER_TESTING_interpreter_lookup_command (struct 
TALER_TESTING_Interpreter *is,
  * Obtain main execution context for the main loop.
  */
 struct GNUNET_CURL_Context *
-TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is)
+TALER_TESTING_interpreter_get_context
+  (struct TALER_TESTING_Interpreter *is)
 {
   return is->ctx;
 }
 
 
 struct TALER_FAKEBANK_Handle *
-TALER_TESTING_interpreter_get_fakebank (struct TALER_TESTING_Interpreter *is)
+TALER_TESTING_interpreter_get_fakebank
+  (struct TALER_TESTING_Interpreter *is)
 {
   return is->fakebank;
 }
 
 
+/**
+ * Run tests starting the "fakebank" first.  The "fakebank"
+ * is a C minimalist version of the human-oriented Python bank,
+ * which is also part of the Taler project.
+ *
+ * @param is pointer to the interpreter state
+ * @param commands the list of commands to execute
+ * @param bank_url the url the fakebank is supposed to run on
+ */
 void
-TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is,
-                                 struct TALER_TESTING_Command *commands,
-                                 const char *bank_url)
+TALER_TESTING_run_with_fakebank
+  (struct TALER_TESTING_Interpreter *is,
+   struct TALER_TESTING_Command *commands,
+   const char *bank_url)
 {
   const char *port;
   long pnum;
@@ -159,7 +114,7 @@ TALER_TESTING_run_with_fakebank (struct 
TALER_TESTING_Interpreter *is,
   else
     pnum = strtol (port + 1, NULL, 10);
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Staring Fakebank on port %u (%s)\n",
+              "Starting Fakebank on port %u (%s)\n",
               (unsigned int) pnum,
               bank_url);
   is->fakebank = TALER_FAKEBANK_start ((uint16_t) pnum);
@@ -192,8 +147,7 @@ TALER_TESTING_interpreter_next (struct 
TALER_TESTING_Interpreter *i)
   if (GNUNET_SYSERR == i->result)
     return; /* ignore, we already failed! */
   i->ip++;
-  i->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
-                                      i);
+  i->task = GNUNET_SCHEDULER_add_now (&interpreter_run, i);
 }
 
 
@@ -201,21 +155,25 @@ TALER_TESTING_interpreter_next (struct 
TALER_TESTING_Interpreter *i)
  * Current command failed, clean up and fail the test case.
  */
 void
-TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *i)
+TALER_TESTING_interpreter_fail
+  (struct TALER_TESTING_Interpreter *is)
 {
-  i->result = GNUNET_SYSERR;
+  // FIXME: disconnect from the exchange.
+  is->result = GNUNET_SYSERR;
+  // this cleans up too.
   GNUNET_SCHEDULER_shutdown ();
 }
 
 
 /**
  * Create command array terminator.
+ *
+ * @return a end-command.
  */
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_end (void)
 {
   static struct TALER_TESTING_Command cmd;
-
   return cmd;
 }
 
@@ -224,7 +182,8 @@ TALER_TESTING_cmd_end (void)
  * Obtain current label.
  */
 const char *
-TALER_TESTING_interpreter_get_current_label (struct TALER_TESTING_Interpreter 
*is)
+TALER_TESTING_interpreter_get_current_label
+  (struct TALER_TESTING_Interpreter *is)
 {
   struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
 
@@ -278,6 +237,10 @@ do_shutdown (void *cls)
   for (unsigned int j=0;NULL != (cmd = &is->commands[j])->label;j++)
     cmd->cleanup (cmd->cls,
                   cmd);
+  if (NULL != is->exchange)
+  {
+    TALER_EXCHANGE_disconnect (is->exchange);
+  }
   if (NULL != is->task)
   {
     GNUNET_SCHEDULER_cancel (is->task);
@@ -351,7 +314,7 @@ maint_child_death (void *cls)
                                        sizeof (c)));
   if (GNUNET_OK !=
       TALER_TESTING_get_trait_process (cmd,
-                                       NULL,
+                                       0,
                                        &processp))
   {
     GNUNET_break (0);
@@ -361,6 +324,14 @@ maint_child_death (void *cls)
   GNUNET_OS_process_wait (*processp);
   GNUNET_OS_process_destroy (*processp);
   *processp = NULL;
+
+  if (GNUNET_OK == is->reload_keys)
+  {
+    GNUNET_break (0 == GNUNET_OS_process_kill
+    (is->exchanged, SIGUSR1));
+    sleep (5); /* make sure signal was received and processed */
+  }
+
   TALER_TESTING_interpreter_next (is);
 }
 
@@ -372,7 +343,8 @@ maint_child_death (void *cls)
  * with the next command.
  */
 void
-TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is)
+TALER_TESTING_wait_for_sigchld
+  (struct TALER_TESTING_Interpreter *is)
 {
   const struct GNUNET_DISK_FileHandle *pr;
 
@@ -384,42 +356,66 @@ TALER_TESTING_wait_for_sigchld (struct 
TALER_TESTING_Interpreter *is)
                                       pr,
                                       &maint_child_death,
                                       is);
-
 }
 
 
+/**
+ * Run the testsuite.  FIXME: explain why the commands are
+ * copied into the state.
+ *
+ * @param is the interpreter state
+ * @param commands the list of command to execute
+ */
 void
 TALER_TESTING_run (struct TALER_TESTING_Interpreter *is,
                    struct TALER_TESTING_Command *commands)
 {
   unsigned int i;
-
+  /* get the number of commands */
   for (i=0;NULL != commands[i].label;i++) ;
+
   is->commands = GNUNET_new_array (i + 1,
                                    struct TALER_TESTING_Command);
   memcpy (is->commands,
           commands,
           sizeof (struct TALER_TESTING_Command) * i);
-  is->timeout_task
-    = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
-                                    (GNUNET_TIME_UNIT_SECONDS, 300),
-                                    &do_timeout,
-                                    is);
-  GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
-                                 is);
-  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
-                                      is);
+  is->timeout_task = GNUNET_SCHEDULER_add_delayed
+    (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300),
+     &do_timeout, is);
+  GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is);
+  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, is);
 }
 
 
+/**
+ * Information used by the wrapper around the main
+ * "run" method.
+ */
 struct MainContext
 {
+  /**
+   * Main "run" method.
+   */
   TALER_TESTING_Main main_cb;
 
+  /**
+   * Closure for "run".
+   */
   void *main_cb_cls;
 
+  /**
+   * Interpreter state.
+   */
   struct TALER_TESTING_Interpreter *is;
 
+  /**
+   * Configuration filename.  The wrapper uses it to fetch
+   * the exchange port number; We could have passed the port
+   * number here, but having the config filename seems more
+   * generic.
+   */
+  const char *config_filename;
+
 };
 
 
@@ -433,58 +429,170 @@ 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)));
+  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 */
 }
 
 
+/**
+ * Called once a connection to the exchange has been
+ * established.
+ *
+ * @param cls closure, typically, the "run" method containing
+ *        all the commands to be run, and a closure for it.
+ * @param keys the exchange's keys.
+ * @param compat protocol compatibility information.
+ */
+void
+cert_cb (void *cls,
+         const struct TALER_EXCHANGE_Keys *keys,
+        enum TALER_EXCHANGE_VersionCompatibility compat)
+{
+  struct MainContext *main_ctx = cls;
+
+  if (NULL == keys)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Got NULL response for /keys\n");
+  
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Got %d DK from /keys\n",
+              keys->num_denom_keys);
+
+  main_ctx->is->key_generation++;
+  main_ctx->is->keys = keys;
+  
+  /* /keys has been called for some reason and
+   * the interpreter is already running. */
+  if (GNUNET_YES == main_ctx->is->working)
+    return;
+
+  main_ctx->is->working = GNUNET_YES;
+
+  /* Very first start of tests, call "run()" */
+  if (1 == main_ctx->is->key_generation)
+  {
+    main_ctx->main_cb (main_ctx->main_cb_cls,
+                       main_ctx->is);
+    return;
+  }
+
+  /* Tests already started, just trigger the
+   * next command. */
+  GNUNET_SCHEDULER_add_now (&interpreter_run,
+                            main_ctx->is);
+}
+
+
+/**
+ * Initialize scheduler loop and curl context for the testcase,
+ * and responsible to run the "run" method.
+ *
+ * @param cls closure, typically the "run" method, the
+ *        interpreter state and a closure for "run".
+ */
 static void
 main_wrapper (void *cls)
 {
   struct MainContext *main_ctx = cls;
   struct TALER_TESTING_Interpreter *is = main_ctx->is;
+  struct GNUNET_CONFIGURATION_Handle *cfg;
+  char *exchange_url;
+  long long unsigned int exchange_port;
 
-  is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
-                              &is->rc);
+  cfg = GNUNET_CONFIGURATION_create ();
+  if (GNUNET_OK != GNUNET_CONFIGURATION_load
+    (cfg, main_ctx->config_filename))
+    return;
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (cfg,
+                                             "exchange",
+                                             "PORT",
+                                             &exchange_port))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "exchange",
+                               "PORT");
+    GNUNET_CONFIGURATION_destroy (cfg);
+    return;
+  }
+  GNUNET_asprintf (&exchange_url,
+                   "http://localhost:%llu/";,
+                   exchange_port);
+
+  is->ctx = GNUNET_CURL_init
+    (&GNUNET_CURL_gnunet_scheduler_reschedule, &is->rc);
   GNUNET_assert (NULL != is->ctx);
   is->rc = GNUNET_CURL_gnunet_rc_create (is->ctx);
-  main_ctx->main_cb (main_ctx->main_cb_cls,
-                     main_ctx->is);
+
+  GNUNET_assert ( NULL !=
+    (is->exchange = TALER_EXCHANGE_connect (is->ctx,
+                                            exchange_url,
+                                            cert_cb,
+                                            main_ctx)) );
+  GNUNET_free (exchange_url);
+  GNUNET_CONFIGURATION_destroy (cfg);
+
 }
 
 
 /**
- * Initialize scheduler loop and curl context for the testcase.
+ * Install signal handlers plus schedules the main wrapper
+ * around the "run" method.
+ *
+ * @param main_cb the "run" method which coontains all the
+ *        commands.
+ * @param main_cb_cls a closure for "run", typically NULL.
+ * @param config_filename configuration filename.
+ * @param exchanged exchange process handle: will be put in the
+ *        state as some commands - e.g. revoke - need to send
+ *        signal to it, for example to let it know to reload the
+ *        key state..
+ *
+ * @return FIXME: not sure what 'is.result' is at this stage.
  */
 int
 TALER_TESTING_setup (TALER_TESTING_Main main_cb,
-                     void *main_cb_cls)
+                     void *main_cb_cls,
+                     const char *config_filename,
+                     struct GNUNET_OS_Process *exchanged)
 {
   struct TALER_TESTING_Interpreter is;
   struct MainContext main_ctx = {
     .main_cb = main_cb,
     .main_cb_cls = main_cb_cls,
-    .is = &is
+    /* needed to init the curl ctx */
+    .is = &is,
+    /* needed to read values like exchange port
+     * number and construct the exchange url.  The
+     * port number _could_ have been passed here, but
+     * we prefer to stay "general" as other values might
+     * need to be passed around in the future. */
+    .config_filename = config_filename
   };
   struct GNUNET_SIGNAL_Context *shc_chld;
-
+  /* zero-ing the state */
   memset (&is,
           0,
           sizeof (is));
+  is.exchanged = exchanged;
   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);
+  shc_chld = GNUNET_SIGNAL_handler_install
+    (GNUNET_SIGCHLD, &sighandler_child_death);
   GNUNET_SCHEDULER_run (&main_wrapper,
                         &main_ctx);
   GNUNET_SIGNAL_handler_uninstall (shc_chld);
   GNUNET_DISK_pipe_close (sigpipe);
   sigpipe = NULL;
+
+  /*FIXME: ?? */
   return is.result;
 }
 
diff --git a/src/exchange-lib/testing_api_trait_blinding_key.c 
b/src/exchange-lib/testing_api_trait_blinding_key.c
index d23fd93..c9415ca 100644
--- a/src/exchange-lib/testing_api_trait_blinding_key.c
+++ b/src/exchange-lib/testing_api_trait_blinding_key.c
@@ -40,22 +40,22 @@
  */
 int
 TALER_TESTING_get_trait_blinding_key (const struct TALER_TESTING_Command *cmd,
-                                      const char *selector,
+                                      unsigned int index,
                                       struct TALER_DenominationBlindingKeyP 
**blinding_key)
 {
   return cmd->traits (cmd->cls,
                       (void **) blinding_key,
                       TALER_TESTING_TRAIT_BLINDING_KEY,
-                      selector);
+                      index);
 }
 
 
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_blinding_key (const char *selector,
+TALER_TESTING_make_trait_blinding_key (unsigned int index,
                                        const struct 
TALER_DenominationBlindingKeyP *blinding_key)
 {
   struct TALER_TESTING_Trait ret = {
-    .selector = selector,
+    .index = index,
     .trait_name = TALER_TESTING_TRAIT_BLINDING_KEY,
     .ptr = (const void *) blinding_key
   };
diff --git a/src/exchange-lib/testing_api_trait_coin_priv.c 
b/src/exchange-lib/testing_api_trait_coin_priv.c
index 6ec4170..522f728 100644
--- a/src/exchange-lib/testing_api_trait_coin_priv.c
+++ b/src/exchange-lib/testing_api_trait_coin_priv.c
@@ -2,18 +2,21 @@
   This file is part of TALER
   Copyright (C) 2018 Taler Systems SA
 
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
+  TALER is 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.
+  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
+  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-lib/testing_api_trait_coin_priv.c
  * @brief main interpreter loop for testcases
@@ -34,28 +37,31 @@
  * Obtain a coin private key from a @a cmd.
  *
  * @param cmd command to extract trait from
- * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param selector which coin to pick if @a cmd has multiple on
+ *        offer
  * @param coin_priv[out] set to the private key of the coin
  * @return #GNUNET_OK on success
  */
 int
-TALER_TESTING_get_trait_coin_priv (const struct TALER_TESTING_Command *cmd,
-                                   const char *selector,
-                                   struct TALER_CoinSpendPrivateKeyP 
**coin_priv)
+TALER_TESTING_get_trait_coin_priv
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   struct TALER_CoinSpendPrivateKeyP **coin_priv)
 {
   return cmd->traits (cmd->cls,
                       (void **) coin_priv,
                       TALER_TESTING_TRAIT_COIN_PRIVATE_KEY,
-                      selector);
+                      index);
 }
 
 
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_coin_priv (const char *selector,
-                                    const struct TALER_CoinSpendPrivateKeyP 
*coin_priv)
+TALER_TESTING_make_trait_coin_priv
+  (unsigned int index,
+   const struct TALER_CoinSpendPrivateKeyP *coin_priv)
 {
   struct TALER_TESTING_Trait ret = {
-    .selector = selector,
+    .index = index,
     .trait_name = TALER_TESTING_TRAIT_COIN_PRIVATE_KEY,
     .ptr = (const void *) coin_priv
   };
diff --git a/src/exchange-lib/testing_api_trait_denom_pub.c 
b/src/exchange-lib/testing_api_trait_denom_pub.c
index 3113818..22d9a34 100644
--- a/src/exchange-lib/testing_api_trait_denom_pub.c
+++ b/src/exchange-lib/testing_api_trait_denom_pub.c
@@ -2,16 +2,18 @@
   This file is part of TALER
   Copyright (C) 2018 Taler Systems SA
 
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
+  TALER is 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.
+  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
+  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/>
 */
 /**
@@ -34,28 +36,39 @@
  * Obtain a denomination public key from a @a cmd.
  *
  * @param cmd command to extract trait from
- * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param selector which coin to pick if @a cmd has multiple on
+ *        offer
  * @param denom_pub[out] set to the blinding key of the coin
  * @return #GNUNET_OK on success
  */
 int
-TALER_TESTING_get_trait_denom_pub (const struct TALER_TESTING_Command *cmd,
-                                   const char *selector,
-                                   struct TALER_EXCHANGE_DenomPublicKey 
**denom_pub)
+TALER_TESTING_get_trait_denom_pub
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   struct TALER_EXCHANGE_DenomPublicKey **denom_pub)
 {
   return cmd->traits (cmd->cls,
                       (void **) denom_pub,
                       TALER_TESTING_TRAIT_DENOM_PUB,
-                      selector);
+                      index);
 }
 
 
+/**
+ * Make a trait for a denomination public key.
+ *
+ * @param selector in case the trait provides multiple
+ *        objects, this parameter extracts a particular one.
+ * @param denom_pub pointer to the data to be returned from
+ *        this trait
+ */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_denom_pub (const char *selector,
-                                    const struct TALER_EXCHANGE_DenomPublicKey 
*denom_pub)
+TALER_TESTING_make_trait_denom_pub
+  (unsigned int index,
+   const struct TALER_EXCHANGE_DenomPublicKey *denom_pub)
 {
   struct TALER_TESTING_Trait ret = {
-    .selector = selector,
+    .index = index,
     .trait_name = TALER_TESTING_TRAIT_DENOM_PUB,
     .ptr = (const void *) denom_pub
   };
@@ -63,5 +76,4 @@ TALER_TESTING_make_trait_denom_pub (const char *selector,
   return ret;
 }
 
-
 /* end of testing_api_trait_denom_pub.c */
diff --git a/src/exchange-lib/testing_api_trait_denom_sig.c 
b/src/exchange-lib/testing_api_trait_denom_sig.c
index 66785c7..9578277 100644
--- a/src/exchange-lib/testing_api_trait_denom_sig.c
+++ b/src/exchange-lib/testing_api_trait_denom_sig.c
@@ -40,22 +40,22 @@
  */
 int
 TALER_TESTING_get_trait_denom_sig (const struct TALER_TESTING_Command *cmd,
-                                   const char *selector,
+                                   unsigned int index,
                                    struct TALER_DenominationSignature 
**denom_sig)
 {
   return cmd->traits (cmd->cls,
                       (void **) denom_sig,
                       TALER_TESTING_TRAIT_DENOM_SIG,
-                      selector);
+                      index);
 }
 
 
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_denom_sig (const char *selector,
+TALER_TESTING_make_trait_denom_sig (unsigned int index,
                                     const struct TALER_DenominationSignature 
*denom_sig)
 {
   struct TALER_TESTING_Trait ret = {
-    .selector = selector,
+    .index = index,
     .trait_name = TALER_TESTING_TRAIT_DENOM_SIG,
     .ptr = (const void *) denom_sig
   };
diff --git a/src/exchange-lib/testing_api_trait_fresh_coin.c 
b/src/exchange-lib/testing_api_trait_fresh_coin.c
new file mode 100644
index 0000000..985591b
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_fresh_coin.c
@@ -0,0 +1,74 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_fresh_coin.c
+ * @brief traits to offer fresh conins (after "melt" operations)
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_FRESH_COINS "fresh-coins"
+
+/**
+ * Obtain a "number" value from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on
+ * offer
+ * @param fresh_coins[out] will point to array of fresh coins
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_fresh_coins
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   struct FreshCoin **fresh_coins)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) fresh_coins,
+                      TALER_TESTING_TRAIT_FRESH_COINS,
+                      index);
+}
+
+/**
+ * @param selector associate the object with this "tag"
+ * @param fresh_coins the array of fresh coins to offer
+ *
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_fresh_coins
+  (unsigned int index,
+   struct FreshCoin *fresh_coins)
+{
+  struct TALER_TESTING_Trait ret = {
+    .index = index,
+    .trait_name = TALER_TESTING_TRAIT_FRESH_COINS,
+    .ptr = (const void *) fresh_coins
+  };
+  return ret;
+}
+
+/* end of testing_api_trait_fresh_coin.c */
diff --git a/src/exchange-lib/testing_api_trait_key_peer.c 
b/src/exchange-lib/testing_api_trait_key_peer.c
new file mode 100644
index 0000000..d4e207c
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_key_peer.c
@@ -0,0 +1,82 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_trait_key_peer.c
+ * @brief traits to offer peer's (private) keys
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+/**
+ * NOTE: calling it "peer" key to make clear it is _not a coin_
+ *       key.
+ */
+
+#define TALER_TESTING_TRAIT_KEY_PEER "key-peer"
+
+/**
+ * Obtain a private key from a "peer".  Used e.g. to obtain
+ * a merchant's priv to sign a /track request.
+ *
+ * @param index (tipically zero) which key to return if they
+ *        exist in an array.
+ * @param selector which coin to pick if @a cmd has multiple on
+ * offer
+ * @param priv[out] set to the key coming from @a cmd.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_peer_key
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   const struct GNUNET_CRYPTO_EddsaPrivateKey **priv)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) priv,
+                      TALER_TESTING_TRAIT_KEY_PEER,
+                      index);
+}
+
+/**
+ * @param index (tipically zero) which key to return if they
+ *        exist in an array.
+ * @param priv which object should be returned
+ *
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_peer_key
+  (unsigned int index,
+   struct GNUNET_CRYPTO_EddsaPrivateKey *priv)
+{
+  struct TALER_TESTING_Trait ret = {
+    .index = index,
+    .trait_name = TALER_TESTING_TRAIT_KEY_PEER,
+    .ptr = (const void *) priv
+  };
+  return ret;
+}
+
+/* end of testing_api_trait_key_peer.c */
diff --git a/src/exchange-lib/testing_api_trait_number.c 
b/src/exchange-lib/testing_api_trait_number.c
new file mode 100644
index 0000000..8f011dc
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_number.c
@@ -0,0 +1,74 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_trait_number.c
+ * @brief traits to offer numbers
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_NUMBER "number"
+
+/**
+ * Obtain a "number" value from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on
+ * offer
+ * @param n[out] set to the number coming from @a cmd.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_uint
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   unsigned int **n)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) n,
+                      TALER_TESTING_TRAIT_NUMBER,
+                      index);
+}
+
+/**
+ * @param selector associate the object with this "tag"
+ * @param n which object should be returned
+ *
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_uint
+  (unsigned int index,
+   const unsigned int *n)
+{
+  struct TALER_TESTING_Trait ret = {
+    .index = index,
+    .trait_name = TALER_TESTING_TRAIT_NUMBER,
+    .ptr = (const void *) n
+  };
+  return ret;
+}
+
+/* end of testing_api_trait_number.c */
diff --git a/src/exchange-lib/testing_api_trait_process.c 
b/src/exchange-lib/testing_api_trait_process.c
index 877eca9..e3c1bdf 100644
--- a/src/exchange-lib/testing_api_trait_process.c
+++ b/src/exchange-lib/testing_api_trait_process.c
@@ -40,22 +40,22 @@
  */
 int
 TALER_TESTING_get_trait_process (const struct TALER_TESTING_Command *cmd,
-                                 const char *selector,
+                                 unsigned int index,
                                  struct GNUNET_OS_Process ***processp)
 {
   return cmd->traits (cmd->cls,
                       (void **) processp,
                       TALER_TESTING_TRAIT_PROCESS,
-                      selector);
+                      index);
 }
 
 
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_process (const char *selector,
+TALER_TESTING_make_trait_process (unsigned int index,
                                   struct GNUNET_OS_Process **processp)
 {
   struct TALER_TESTING_Trait ret = {
-    .selector = selector,
+    .index = index,
     .trait_name = TALER_TESTING_TRAIT_PROCESS,
     .ptr = (const void *) processp
   };
diff --git a/src/exchange-lib/testing_api_trait_reserve_priv.c 
b/src/exchange-lib/testing_api_trait_reserve_priv.c
index a849f76..fb80a06 100644
--- a/src/exchange-lib/testing_api_trait_reserve_priv.c
+++ b/src/exchange-lib/testing_api_trait_reserve_priv.c
@@ -39,22 +39,22 @@
  */
 int
 TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd,
-                                      const char *selector,
+                                      unsigned int index,
                                       struct TALER_ReservePrivateKeyP 
**reserve_priv)
 {
   return cmd->traits (cmd->cls,
                       (void **) reserve_priv,
                       TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY,
-                      selector);
+                      index);
 }
 
 
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_reserve_priv (const char *selector,
+TALER_TESTING_make_trait_reserve_priv (unsigned int index,
                                        const struct TALER_ReservePrivateKeyP 
*reserve_priv)
 {
   struct TALER_TESTING_Trait ret = {
-    .selector = selector,
+    .index = index,
     .trait_name = TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY,
     .ptr = (const void *) reserve_priv
   };
diff --git a/src/exchange-lib/testing_api_trait_string.c 
b/src/exchange-lib/testing_api_trait_string.c
new file mode 100644
index 0000000..308c4ea
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_string.c
@@ -0,0 +1,209 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_trait_string.c
+ * @brief offers strings traits.  Mostly used to offer
+ *        stringified JSONs.
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_WIRE_DETAILS "wire-details"
+#define TALER_TESTING_TRAIT_CONTRACT_TERMS "contract-terms"
+#define TALER_TESTING_TRAIT_TRANSFER_SUBJECT "transfer-subject"
+#define TALER_TESTING_TRAIT_AMOUNT "amount"
+
+/**
+ * Obtain contract terms from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param index always (?) zero, as one command sticks
+ *        to one bank account
+ * @param contract_terms[out] where to write the contract
+ *        terms.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_contract_terms
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   const char **contract_terms)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) contract_terms,
+                      TALER_TESTING_TRAIT_CONTRACT_TERMS,
+                      index);
+}
+
+/**
+ * @param index always (?) zero, as one command sticks
+ *        to one bank account
+ * @param contract_terms contract terms to offer
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_contract_terms
+  (unsigned int index,
+   const char *contract_terms)
+{
+  struct TALER_TESTING_Trait ret = {
+    .index = index,
+    .trait_name = TALER_TESTING_TRAIT_CONTRACT_TERMS,
+    .ptr = (const void *) contract_terms
+  };
+  return ret;
+}
+
+
+/**
+ * Obtain wire details from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param index always (?) zero, as one command sticks
+ *        to one bank account
+ * @param wire_details[out] where to write the wire details.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_wire_details
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   const char **wire_details)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) wire_details,
+                      TALER_TESTING_TRAIT_WIRE_DETAILS,
+                      index);
+}
+
+/**
+ * Offer wire details in a trait.
+ *
+ * @param index always (?) zero, as one command sticks
+ *        to one bank account
+ * @param wire_details wire details to offer
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_wire_details
+  (unsigned int index,
+   const char *wire_details)
+{
+  struct TALER_TESTING_Trait ret = {
+    .index = index,
+    .trait_name = TALER_TESTING_TRAIT_WIRE_DETAILS,
+    .ptr = (const void *) wire_details
+  };
+  return ret;
+}
+
+
+/**
+ * Obtain a transfer subject from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param index always (?) zero, as one command sticks
+ *        to one bank transfer
+ * @param transfer_subject[out] where to write the wire details.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_transfer_subject
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   char **transfer_subject)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) transfer_subject,
+                      TALER_TESTING_TRAIT_TRANSFER_SUBJECT,
+                      index);
+}
+
+/**
+ * Offer wire details in a trait.
+ *
+ * @param index always (?) zero, as one command sticks
+ *        to one bank account
+ * @param wire_details wire details to offer
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_transfer_subject
+  (unsigned int index,
+   char *transfer_subject)
+{
+  struct TALER_TESTING_Trait ret = {
+    .index = index,
+    .trait_name = TALER_TESTING_TRAIT_TRANSFER_SUBJECT,
+    .ptr = (const void *) transfer_subject
+  };
+  return ret;
+}
+
+
+/**
+ * Obtain an amount from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param index which amount is to be picked, in case
+ *        multiple are offered.
+ * @param amount[out] where to write the wire details.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_amount
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   const char **amount)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) amount,
+                      TALER_TESTING_TRAIT_AMOUNT,
+                      index);
+}
+
+/**
+ * Offer amount in a trait.
+ *
+ * @param index which amount is to be picked, in case
+ *        multiple are offered.
+ * @param amount the amount to offer
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_amount
+  (unsigned int index,
+   const char *amount)
+{
+  struct TALER_TESTING_Trait ret = {
+    .index = index,
+    .trait_name = TALER_TESTING_TRAIT_AMOUNT,
+    .ptr = (const void *) amount
+  };
+  return ret;
+}
+
+
+/* end of testing_api_trait_string.c */
diff --git a/src/exchange-lib/testing_api_trait_wtid.c 
b/src/exchange-lib/testing_api_trait_wtid.c
new file mode 100644
index 0000000..140a2ac
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_wtid.c
@@ -0,0 +1,74 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_trait_number.c
+ * @brief traits to offer numbers
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_WTID "wtid"
+
+/**
+ * Obtain a WTID value from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param index which WTID to pick if @a cmd has multiple on
+ *        offer
+ * @param wtid[out] set to the wanted WTID.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_wtid
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   struct TALER_WireTransferIdentifierRawP **wtid)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) wtid,
+                      TALER_TESTING_TRAIT_WTID,
+                      index);
+}
+
+/**
+ * @param index associate the object with this index
+ * @param wtid which object should be returned
+ *
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_wtid
+  (unsigned int index,
+   struct TALER_WireTransferIdentifierRawP *wtid)
+{
+  struct TALER_TESTING_Trait ret = {
+    .index = index,
+    .trait_name = TALER_TESTING_TRAIT_WTID,
+    .ptr = (const void *) wtid
+  };
+  return ret;
+}
+
+/* end of testing_api_trait_number.c */
diff --git a/src/exchange-lib/testing_api_traits.c 
b/src/exchange-lib/testing_api_traits.c
index 3983d32..c83c7e7 100644
--- a/src/exchange-lib/testing_api_traits.c
+++ b/src/exchange-lib/testing_api_traits.c
@@ -2,16 +2,18 @@
   This file is part of TALER
   Copyright (C) 2018 Taler Systems SA
 
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
+  TALER is 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.
+  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
+  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/>
 */
 /**
@@ -28,11 +30,15 @@
 #include "taler_testing_lib.h"
 
 
+/**
+ * End a trait array.  Usually, commands offer several traits,
+ * and put them in arrays.
+ */
 struct TALER_TESTING_Trait
 TALER_TESTING_trait_end ()
 {
   struct TALER_TESTING_Trait end = {
-    .selector = NULL,
+    .index = 0,
     .trait_name = NULL,
     .ptr = NULL
   };
@@ -40,31 +46,35 @@ TALER_TESTING_trait_end ()
   return end;
 }
 
-
+/**
+ * Pick the chosen trait from the traits array.
+ *
+ * @param traits the traits array
+ * @param ret where to store the result
+ * @param selector which particular object in the trait should be
+ *        returned
+ *
+ * @return GNUNET_OK if no error occurred, GNUNET_SYSERR otherwise
+ */
 int
 TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
                          void **ret,
                          const char *trait,
-                         const char *selector)
+                         unsigned int index)
 {
-  for (unsigned int i=0;
-       NULL != traits[i].trait_name;
-       i++)
+  for (unsigned int i=0; NULL != traits[i].trait_name; i++)
   {
-    if ( (0 == strcmp (trait,
-                       traits[i].trait_name)) &&
-         ( (NULL == selector) ||
-           (0 == strcasecmp (selector,
-                             traits[i].selector) ) ) )
+    if ( (0 == strcmp (trait, traits[i].trait_name)) &&
+         (index == traits[i].index) )
     {
       *ret = (void *) traits[i].ptr;
       return GNUNET_OK;
     }
   }
-  /* FIXME: log */
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+              "Trait %s/%u not found.\n",
+              trait, index);
   return GNUNET_SYSERR;
 }
 
-
-
 /* end of testing_api_traits.c */
diff --git a/src/exchange-tools/taler-exchange-keyup.c 
b/src/exchange-tools/taler-exchange-keyup.c
index b37a7a4..5e069af 100644
--- a/src/exchange-tools/taler-exchange-keyup.c
+++ b/src/exchange-tools/taler-exchange-keyup.c
@@ -1124,6 +1124,9 @@ exchange_keys_revoke_by_dki (void *cls,
     rc->ok = GNUNET_SYSERR;
     return GNUNET_SYSERR;
   }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Successfully revoking denom '%s..'\n",
+              TALER_B2S (rc->hc));
   return GNUNET_NO;
 }
 
diff --git a/src/exchange/taler-exchange-httpd_keystate.c 
b/src/exchange/taler-exchange-httpd_keystate.c
index f57451a..0428b10 100644
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ b/src/exchange/taler-exchange-httpd_keystate.c
@@ -1682,6 +1682,10 @@ TEH_KS_denomination_key_lookup (const struct 
TEH_KS_StateHandle *key_state,
 
   GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
                                      &hc);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Looking for denom: '%s..'\n",
+              TALER_B2S (&hc));
+
   return TEH_KS_denomination_key_lookup_by_hash (key_state,
                                                  &hc,
                                                  use);
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index 2a95ff6..de671c9 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -2,16 +2,19 @@
   This file is part of TALER
   (C) 2018 Taler Systems SA
 
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along with
-  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+  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/>
 */
 
 /**
@@ -29,7 +32,21 @@
 #include <microhttpd.h>
 
 
-/* ********************* Helper functions *********************** */
+/* ********************* Helper functions ********************* */
+
+/**
+ * Allocate and return a piece of wire-details.  Mostly, it adds
+ * the bank_url to the JSON.
+ *
+ * @param template the wire-details template.
+ * @param bank_url the bank_url
+ *
+ * @return the filled out and stringified wire-details.  To
+ *         be manually free'd.
+ */
+char *
+TALER_TESTING_make_wire_details (const char *template,
+                                 const char *bank_url);
 
 /**
  * Find denomination key matching the given amount.
@@ -50,8 +67,8 @@ TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,
  * launch the exchange process itself.
  *
  * @param config_filename configuration file to use
- * @return #GNUNET_OK on success, #GNUNET_NO if test should be skipped,
- *         #GNUNET_SYSERR on test failure
+ * @return #GNUNET_OK on success, #GNUNET_NO if test should be
+ *         skipped, #GNUNET_SYSERR on test failure
  */
 int
 TALER_TESTING_prepare_exchange (const char *config_filename);
@@ -83,13 +100,100 @@ char *
 TALER_TESTING_prepare_fakebank (const char *config_filename);
 
 
-/* ******************* Generic interpreter logic ****************** */
+/* ******************* Generic interpreter logic ************ */
 
 /**
  * Global state of the interpreter, used by a command
  * to access information about other commands.
  */
-struct TALER_TESTING_Interpreter;
+struct TALER_TESTING_Interpreter
+{
+
+  /**
+   * Commands the interpreter will run.
+   */
+  struct TALER_TESTING_Command *commands;
+
+  /**
+   * Interpreter task (if one is scheduled).
+   */
+  struct GNUNET_SCHEDULER_Task *task;
+
+  /**
+   * ID of task called whenever we get a SIGCHILD.
+   * Used for #TALER_TESTING_wait_for_sigchld().
+   */
+  struct GNUNET_SCHEDULER_Task *child_death_task;
+
+  /**
+   * Main execution context for the main loop.
+   */
+  struct GNUNET_CURL_Context *ctx;
+
+  /**
+   * Context for running the CURL event loop.
+   */
+  struct GNUNET_CURL_RescheduleContext *rc;
+
+  /**
+   * Handle to our fakebank, if #TALER_TESTING_run_with_fakebank() was used.
+   * Otherwise NULL.
+   */
+  struct TALER_FAKEBANK_Handle *fakebank;
+
+  /**
+   * Task run on timeout.
+   */
+  struct GNUNET_SCHEDULER_Task *timeout_task;
+
+  /**
+   * Instruction pointer.  Tells #interpreter_run() which
+   * instruction to run next.
+   */
+  unsigned int ip;
+
+  /**
+   * Result of the testcases, #GNUNET_OK on success
+   */
+  int result;
+
+  /**
+   * Handle to the exchange.
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * Handle to exchange process; some commands need it
+   * to send signals.  E.g. to trigger the key state reload.
+   */
+  struct GNUNET_OS_Process *exchanged;
+
+  /**
+   * GNUNET_OK if key state should be reloaded.  NOTE: this
+   * field can be removed because a new "send signal" command
+   * has been introduced.
+   */
+  int reload_keys;
+
+  /**
+   * Is the interpreter running (#GNUNET_YES) or waiting
+   * for /keys (#GNUNET_NO)?
+   */
+  int working;
+
+  /**
+   * How often have we gotten a /keys response so far?
+   */
+  unsigned int key_generation;
+
+  /**
+   * Exchange keys from last download.
+   */
+  const struct TALER_EXCHANGE_Keys *keys;
+
+};
+
+
 
 
 /**
@@ -143,66 +247,73 @@ struct TALER_TESTING_Command
    * @param ret[out] result (could be anything)
    * @param trait name of the trait
    * @param selector more detailed information about which object
-   *                 to return in case there were multiple generated
-   *                 by the command
+   *                 to return in case there were multiple
+   *                 generated by the command
    * @return #GNUNET_OK on success
    */
   int
   (*traits)(void *cls,
             void **ret,
             const char *trait,
-            const char *selector);
+            unsigned int index);
 
 };
 
-
 /**
  * Lookup command by label.
  */
 const struct TALER_TESTING_Command *
-TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *i,
-                                          const char *label);
-
+TALER_TESTING_interpreter_lookup_command
+  (struct TALER_TESTING_Interpreter *i,
+   const char *label);
 
 /**
  * Obtain main execution context for the main loop.
  */
 struct GNUNET_CURL_Context *
-TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is);
+TALER_TESTING_interpreter_get_context
+  (struct TALER_TESTING_Interpreter *is);
 
 /**
  * Obtain current label.
  */
 const char *
-TALER_TESTING_interpreter_get_current_label (struct TALER_TESTING_Interpreter 
*is);
+TALER_TESTING_interpreter_get_current_label
+  (struct TALER_TESTING_Interpreter *is);
 
 /**
  * Obtain main execution context for the main loop.
  */
 struct GNUNET_CURL_Context *
-TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is);
+TALER_TESTING_interpreter_get_context
+  (struct TALER_TESTING_Interpreter *is);
 
 
 struct TALER_FAKEBANK_Handle *
-TALER_TESTING_interpreter_get_fakebank (struct TALER_TESTING_Interpreter *is);
+TALER_TESTING_interpreter_get_fakebank
+  (struct TALER_TESTING_Interpreter *is);
 
 /**
  * Current command is done, run the next one.
  */
 void
-TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is);
+TALER_TESTING_interpreter_next
+  (struct TALER_TESTING_Interpreter *is);
 
 /**
  * Current command failed, clean up and fail the test case.
  */
 void
-TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is);
+TALER_TESTING_interpreter_fail
+  (struct TALER_TESTING_Interpreter *is);
 
 /**
  * Create command array terminator.
+ *
+ * @return a end-command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_end (void);
+TALER_TESTING_cmd_end ();
 
 
 /**
@@ -212,7 +323,8 @@ TALER_TESTING_cmd_end (void);
  * with the next command.
  */
 void
-TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is);
+TALER_TESTING_wait_for_sigchld
+  (struct TALER_TESTING_Interpreter *is);
 
 
 void
@@ -222,22 +334,39 @@ TALER_TESTING_run (struct TALER_TESTING_Interpreter *is,
 
 
 void
-TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is,
-                                 struct TALER_TESTING_Command *commands,
-                                 const char *bank_url);
+TALER_TESTING_run_with_fakebank
+  (struct TALER_TESTING_Interpreter *is,
+   struct TALER_TESTING_Command *commands,
+   const char *bank_url);
 
 
+/**
+ * FIXME
+ */
 typedef void
 (*TALER_TESTING_Main)(void *cls,
                       struct TALER_TESTING_Interpreter *is);
 
-
 /**
- * Initialize scheduler loop and curl context for the testcase.
+ * Install signal handlers plus schedules the main wrapper
+ * around the "run" method.
+ *
+ * @param main_cb the "run" method which coontains all the
+ *        commands.
+ * @param main_cb_cls a closure for "run", typically NULL.
+ * @param config_filename configuration filename.
+ * @param exchanged exchange process handle: will be put in the
+ *        state as some commands - e.g. revoke - need to send
+ *        signal to it, for example to let it know to reload the
+ *        key state..
+ *
+ * @return FIXME: not sure what 'is.result' is at this stage.
  */
 int
 TALER_TESTING_setup (TALER_TESTING_Main main_cb,
-                     void *main_cb_cls);
+                     void *main_cb_cls,
+                     const char *config_filename,
+                     struct GNUNET_OS_Process *exchanged);
 
 
 /**
@@ -253,7 +382,7 @@ TALER_TESTING_setup_with_exchange (TALER_TESTING_Main 
main_cb,
 
 
 
-/* ****************** Specific interpreter commands **************** */
+/* ************** Specific interpreter commands ************ */
 
 /**
  * Perform a wire transfer (formerly Admin-add-incoming)
@@ -275,14 +404,15 @@ TALER_TESTING_cmd_fakebank_transfer (const char *label,
  *
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label,
-                                                  const char *amount,
-                                                  const char *bank_url,
-                                                  uint64_t debit_account_no,
-                                                  uint64_t credit_account_no,
-                                                  const char *auth_username,
-                                                  const char *auth_password,
-                                                  const char *subject);
+TALER_TESTING_cmd_fakebank_transfer_with_subject
+  (const char *label,
+   const char *amount,
+   const char *bank_url,
+   uint64_t debit_account_no,
+   uint64_t credit_account_no,
+   const char *auth_username,
+   const char *auth_password,
+   const char *subject);
 
 
 /**
@@ -290,24 +420,65 @@ TALER_TESTING_cmd_fakebank_transfer_with_subject (const 
char *label,
  *
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label,
-                                              const char *amount,
-                                              const char *bank_url,
-                                              uint64_t debit_account_no,
-                                              uint64_t credit_account_no,
-                                              const char *auth_username,
-                                              const char *auth_password,
-                                              const char *ref);
+TALER_TESTING_cmd_fakebank_transfer_with_ref
+  (const char *label,
+   const char *amount,
+   const char *bank_url,
+   uint64_t debit_account_no,
+   uint64_t credit_account_no,
+   const char *auth_username,
+   const char *auth_password,
+   const char *ref);
 
 
 /**
  * Execute taler-exchange-wirewatch process.
  *
+ * @param label command label
+ * @param config_filanem configuration filename.
+ *
+ * @return the command.
  */
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_exec_wirewatch (const char *label,
                                   const char *config_filename);
 
+/**
+ * Execute taler-exchange-aggregator process.
+ *
+ * @param label command label
+ * @param config_filename configuration filename
+ *
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_aggregator (const char *label,
+                                   const char *config_filename);
+
+/**
+ * Execute taler-exchange-keyup process.
+ *
+ * @param label command label
+ * @param config_filename configuration filename
+ *
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_keyup (const char *label,
+                              const char *config_filename);
+
+/**
+ * Execute taler-auditor-sign process.
+ *
+ * @param label command label
+ * @param config_filename configuration filename
+ *
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_auditor_sign (const char *label,
+                                     const char *config_filename);
+
 
 /**
  * Create withdraw command.
@@ -315,12 +486,12 @@ TALER_TESTING_cmd_exec_wirewatch (const char *label,
  * @return NULL on failure
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_withdraw_amount (const char *label,
-                                   struct TALER_EXCHANGE_Handle *exchange,
-                                   const char *reserve_reference,
-                                   const char *amount,
-                                   unsigned int expected_response_code);
-
+TALER_TESTING_cmd_withdraw_amount
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *reserve_reference,
+   const char *amount,
+   unsigned int expected_response_code);
 
 
 /**
@@ -328,21 +499,340 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
  *
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_withdraw_denomination (const char *label,
-                                         struct TALER_EXCHANGE_Handle 
*exchange,
-                                         const char *reserve_reference,
-                                         const struct 
TALER_EXCHANGE_DenomPublicKey *dk,
-                                         unsigned int expected_response_code);
+TALER_TESTING_cmd_withdraw_denomination
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *reserve_reference,
+   const struct TALER_EXCHANGE_DenomPublicKey *dk,
+   unsigned int expected_response_code);
+
 
+/**
+ * Create a /wire command.
+ *
+ * @param label the command label.
+ * @param exchange the exchange to connect to.
+ * @param expected_method which wire-transfer method is expected
+ *        to be offered by the exchange.
+ * @param expected_fee the fee the exchange should charge.
+ * @param expected_response_code the HTTP response the exchange
+ *        should return.
+ *
+ * @return the command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_wire (const char *label,
+                        struct TALER_EXCHANGE_Handle *exchange,
+                        const char *expected_method,
+                        const char *expected_fee,
+                        unsigned int expected_response_code);
 
-/* ********************** Generic trait logic for implementing traits 
******************* */
+
+/**
+ * Create a /reserve/status command.
+ *
+ * @param label the command label.
+ * @param exchange the exchange to connect to.
+ * @param reserve_reference reference to the reserve to check.
+ * @param expected_balance balance expected to be at the
+ * referenced reserve.
+ * @param expected_response_code expected HTTP response code.
+ *
+ * @return the command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_status (const char *label,
+                          struct TALER_EXCHANGE_Handle *exchange,
+                          const char *reserve_reference,
+                          const char *expected_balance,
+                          unsigned int expected_response_code);
+
+/**
+ * Create a deposit command.
+ *
+ * @param label command label
+ * @param exchange exchange connection
+ * @param coin_reference reference to any operation that can
+ *        provide a coin
+ * @param coin_index if @a withdraw_reference offers an array of
+ *        coins, this parameter selects which one in that array
+ *        This value is currently ignored, as only one-coin
+ *        withdrawals are implemented.
+ * @param wire_details bank details of the merchant performing the
+ *        deposit
+ * @param contract_terms contract terms to be signed over by the
+ *        coin
+ * @param refund_deadline refund deadline
+ * @param amount how much is going to be deposited
+ * @param expected_response_code which HTTP status code we expect
+ *        in the response
+ *
+ * @return the deposit command to be run by the interpreter
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_deposit 
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *coin_reference,
+   unsigned int coin_index,
+   char *wire_details,
+   const char *contract_terms,
+   struct GNUNET_TIME_Relative refund_deadline,
+   const char *amount,
+   unsigned int expected_response_code);
+
+
+/**
+ * Create a "refresh melt" command.
+ *
+ * @param label command label
+ * @param exchange connection to the exchange
+ * @param amount Fixme
+ * @param coin_reference reference to a command that will provide
+ *        a coin to refresh
+ * @param expected_response_code expected HTTP code
+ */
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_refresh_melt
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *amount,
+   const char *coin_reference,
+   unsigned int expected_response_code);
+
+
+/**
+ * Create a "refresh reveal" command.
+ *
+ * @param label command label
+ * @param exchange connection to the exchange
+ * @param melt_reference reference to a "refresh melt" command
+ * @param expected_response_code expected HTTP response code
+ *
+ * @return the "refresh reveal" command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_refresh_reveal
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *melt_reference,
+   unsigned int expected_response_code);
+
+
+/**
+ * Create a "refresh link" command.
+ *
+ * @param label command label
+ * @param exchange connection to the exchange
+ * @param melt_reference reference to a "refresh melt" command
+ * @param expected_response_code expected HTTP response code
+ *
+ * @return the "refresh link" command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_refresh_link
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *reveal_reference,
+   unsigned int expected_response_code);
+
+
+/**
+ * Create a /track/transaction command.
+ *
+ * @param label the command label.
+ * @param exchange the exchange to connect to.
+ * @param transaction_reference reference to a deposit operation.
+ * @param coin_index index of the coin involved in the transaction
+ * @param expected_response_code expected HTTP response code.
+ * @param bank_transfer_reference which #OC_CHECK_BANK_TRANSFER
+ *        wtid should this match? NULL
+   * for none
+ *
+ * @return the command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_track_transaction
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *transaction_reference,
+   unsigned int coin_index,
+   unsigned int expected_response_code,
+   const char *bank_transfer_reference);
+
+/**
+ * Make a /track/transfer command, expecting the transfer
+ * not being done (yet).
+ *
+ * @param label the command label
+ * @param exchange connection to the exchange
+ * @param wtid_reference reference to any command which can provide
+ *        a wtid
+ * @param index in case there are multiple wtid offered, this
+ *        parameter selects a particular one
+ * @param expected_response_code expected HTTP response code
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_track_transfer_empty
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *wtid_reference,
+   unsigned int index,
+   unsigned int expected_response_code);
+
+
+/**
+ * Make a /track/transfer command, specifying which amount and
+ * wire fee are expected.
+ *
+ * @param label the command label
+ * @param exchange connection to the exchange
+ * @param wtid_reference reference to any command which can provide
+ *        a wtid
+ * @param index in case there are multiple wtid offered, this
+ *        parameter selects a particular one
+ * @param expected_response_code expected HTTP response code
+ * @param expected_amount how much money we expect being
+ *        moved with this wire-transfer.
+ * @param expected_wire_fee expected wire fee.
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_track_transfer
+  (const char *label,
+   struct TALER_EXCHANGE_Handle *exchange,
+   const char *wtid_reference,
+   unsigned int index,
+   unsigned int expected_response_code,
+   const char *expected_total_amount,
+   const char *expected_wire_fee);
+
+/**
+ * Command to check whether a particular wire transfer has been
+ * made or not.
+ *
+ * @param label the command label
+ * @param exchange_base_url base url of the exchange (Fixme: why?)
+ * @param amount the amount expected to be transferred
+ * @param debit_account the account that gave money
+ * @param credit_account the account that received money
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_check_bank_transfer
+  (const char *label,
+   const char *exchange_base_url,
+   const char *amount,
+   unsigned int debit_account,
+   unsigned int credit_account);
+
+/**
+ * Check bank's balance is zero.
+ *
+ * @param credit_account the account that received money
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_check_bank_empty (const char *label);
+
+/**
+ * Create a /refund test command.
+ *
+ * @param label command label
+ * @param expected_response_code expected HTTP status code
+ * @param refund_amount the amount to ask a refund for
+ * @param refund_fee expected refund fee
+ * @param coin_reference reference to a command that can
+ *        provide a coin to be refunded.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_refund (const char *label,
+                          unsigned int expected_response_code,
+                          const char *refund_amount,
+                          const char *refund_fee,
+                          const char *deposit_reference);
+
+
+/**
+ * Make a /payback command.
+ *
+ * @param label the command label
+ * @param expected_response_code expected HTTP status code
+ * @param coin_reference reference to any command which offers
+ *        a reserve private key plus a coin to be paid back.
+ * @param amount denomination to pay back.
+ *
+ * @return a /revoke command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_payback (const char *label,
+                           unsigned int expected_response_code,
+                           const char *coin_reference,
+                           const char *amount);
+
+
+/**
+ * Make a /revoke command.
+ *
+ * @param label the command label
+ * @param expected_response_code expected HTTP status code
+ * @param coin_reference reference to any command which offers
+ *        a coin trait
+ * @param config_filename configuration file name.
+ *
+ * @return a /revoke command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_revoke (const char *label,
+                          unsigned int expected_response_code,
+                          const char *coin_reference,
+                          const char *config_filename);
+
+/**
+ * Send a signal to a process.
+ *
+ * @param label command label
+ * @param process handle to the process
+ * @param signal signal to send
+ *
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_signal (const char *label,
+                          struct GNUNET_OS_Process *process,
+                          int signal);
+
+/**
+ * Make a "check keys" command.
+ *
+ * @param label command label
+ * @param generation FIXME
+ * @param num_denom_keys FIXME
+ * @param exchange connection to the exchange
+ *
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_check_keys
+  (const char *label,
+   unsigned int generation,
+   unsigned int num_denom_keys,
+   struct TALER_EXCHANGE_Handle *exchange);
+
+/* *** Generic trait logic for implementing traits ********* */
 
 /**
  * A trait.
  */
 struct TALER_TESTING_Trait
 {
-  const char *selector;
+  unsigned int index;
 
   const char *trait_name;
 
@@ -359,150 +849,406 @@ int
 TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
                          void **ret,
                          const char *trait,
-                         const char *selector);
+                         unsigned int index);
 
 
-/* ****************** Specific traits supported by this component 
*************** */
+/* ****** Specific traits supported by this component ******* */
 
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_reserve_priv (const char *selector,
-                                       const struct TALER_ReservePrivateKeyP 
*reserve_priv);
+TALER_TESTING_make_trait_reserve_priv
+  (unsigned int index,
+   const struct TALER_ReservePrivateKeyP *reserve_priv);
 
 
 /**
  * Obtain a reserve private key from a @a cmd.
  *
  * @param cmd command to extract trait from
- * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param selector which coin to pick if @a cmd has multiple on
+ * offer
  * @param reserve_priv[out] set to the private key of the reserve
  * @return #GNUNET_OK on success
  */
 int
-TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd,
-                                      const char *selector,
-                                      struct TALER_ReservePrivateKeyP 
**reserve_priv);
-
+TALER_TESTING_get_trait_reserve_priv
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   struct TALER_ReservePrivateKeyP **reserve_priv);
 
 
 /**
  * Obtain location where a command stores a pointer to a process
  *
  * @param cmd command to extract trait from
- * @param selector which process to pick if @a cmd has multiple on offer
- * @param coin_priv[out] set to address of the pointer to the process
+ * @param selector which process to pick if @a cmd has multiple
+ * on offer
+ * @param coin_priv[out] set to address of the pointer to the
+ * process
  * @return #GNUNET_OK on success
  */
 int
-TALER_TESTING_get_trait_process (const struct TALER_TESTING_Command *cmd,
-                                 const char *selector,
-                                 struct GNUNET_OS_Process ***processp);
+TALER_TESTING_get_trait_process
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   struct GNUNET_OS_Process ***processp);
 
 
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_process
+  (unsigned int index,
+   struct GNUNET_OS_Process **processp);
 
 
+/**
+ * @param selector FIXME
+ */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_process (const char *selector,
-                                  struct GNUNET_OS_Process **processp);
+TALER_TESTING_make_trait_coin_priv
+  (unsigned int index,
+   const struct TALER_CoinSpendPrivateKeyP *coin_priv);
 
+/**
+ * Obtain a coin private key from a @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on
+ * offer
+ * @param coin_priv[out] set to the private key of the coin
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_coin_priv
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   struct TALER_CoinSpendPrivateKeyP **coin_priv);
 
 /**
- * @param selector
+ * @param selector a "tag" to associate the object with
+ * @param blinding_key which object should be returned
+ *
+ * @return the trait, to be put in the traits array of the command
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_coin_priv (const char *selector,
-                                    const struct TALER_CoinSpendPrivateKeyP 
*coin_priv);
+TALER_TESTING_make_trait_blinding_key
+  (unsigned int index,
+   const struct TALER_DenominationBlindingKeyP *blinding_key);
+
+/**
+ * Obtain a coin's blinding key from a @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on
+ * offer
+ * @param blinding_key[out] set to the blinding key of the coin
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_blinding_key
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   struct TALER_DenominationBlindingKeyP **blinding_key);
 
+/**
+ * @param selector a "tag" to associate the object with
+ * @param pdk which object should be returned
+ *
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_denom_pub
+  (unsigned int index,
+   const struct TALER_EXCHANGE_DenomPublicKey *dpk);
 
 /**
  * Obtain a coin private key from a @a cmd.
  *
  * @param cmd command to extract trait from
- * @param selector which coin to pick if @a cmd has multiple on offer
- * @param coin_priv[out] set to the private key of the coin
+ * @param selector which coin to pick if @a cmd has multiple on
+ *        offer
+ * @param dpk[out] set to a denomination key of the coin
  * @return #GNUNET_OK on success
  */
 int
-TALER_TESTING_get_trait_coin_priv (const struct TALER_TESTING_Command *cmd,
-                                   const char *selector,
-                                   struct TALER_CoinSpendPrivateKeyP 
**coin_priv);
+TALER_TESTING_get_trait_denom_pub
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   struct TALER_EXCHANGE_DenomPublicKey **dpk);
 
 
+/**
+ * Obtain a coin denomination signature from a @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on
+ * offer
+ * @param sig[out] set to a denomination signature over the coin
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_denom_sig
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   struct TALER_DenominationSignature **dpk);
 
 /**
  * @param selector
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_blinding_key (const char *selector,
-                                       const struct 
TALER_DenominationBlindingKeyP *blinding_key);
+TALER_TESTING_make_trait_denom_sig
+  (unsigned int index,
+   const struct TALER_DenominationSignature *sig);
 
+/**
+ * @param selector associate the object with this "tag"
+ * @param i which object should be returned
+ *
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_uint
+  (unsigned int index,
+   const unsigned int *i);
 
 /**
- * Obtain a coin's blinding key from a @a cmd.
+ * Obtain a "number" value from @a cmd.
  *
  * @param cmd command to extract trait from
- * @param selector which coin to pick if @a cmd has multiple on offer
- * @param blinding_key[out] set to the blinding key of the coin
+ * @param selector which coin to pick if @a cmd has multiple on
+ * offer
+ * @param n[out] set to the number coming from @a cmd.
  * @return #GNUNET_OK on success
  */
 int
-TALER_TESTING_get_trait_blinding_key (const struct TALER_TESTING_Command *cmd,
-                                      const char *selector,
-                                      struct TALER_DenominationBlindingKeyP 
**blinding_key);
+TALER_TESTING_get_trait_uint
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   unsigned int **n);
 
+/**
+ * Information about a fresh coin generated by the refresh
+ * operation. FIXME: should go away from here!
+ */
+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;
+};
 
 /**
- * @param selector
+ * @param selector associate the object with this "tag"
+ * @param fresh_coins array of fresh coins to return
+ *
+ * @return the trait, to be put in the traits array of the command
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_denom_pub (const char *selector,
-                                    const struct TALER_EXCHANGE_DenomPublicKey 
*dpk);
+TALER_TESTING_make_trait_fresh_coins
+  (unsigned int index,
+   struct FreshCoin *fresh_coins);
+
+/**
+ * Obtain a "number" value from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on
+ * offer
+ * @param fresh_coins[out] will point to array of fresh coins
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_fresh_coins
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   struct FreshCoin **fresh_coins);
+
 
 
 /**
- * Obtain a coin private key from a @a cmd.
+ * Obtain contract terms from @a cmd.
  *
  * @param cmd command to extract trait from
- * @param selector which coin to pick if @a cmd has multiple on offer
- * @param dpk[out] set to a denomination key of the coin
+ * @param index always (?) zero, as one command sticks
+ *        to one bank account
+ * @param contract_terms[out] where to write the contract
+ *        terms.
  * @return #GNUNET_OK on success
  */
 int
-TALER_TESTING_get_trait_denom_pub (const struct TALER_TESTING_Command *cmd,
-                                   const char *selector,
-                                   struct TALER_EXCHANGE_DenomPublicKey **dpk);
+TALER_TESTING_get_trait_contract_terms
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   const char **contract_terms);
+
+/**
+ * @param index always (?) zero, as one command sticks
+ *        to one bank account
+ * @param contract_terms contract terms to offer
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_contract_terms
+  (unsigned int index,
+   const char *contract_terms);
 
 
 /**
- * Obtain a coin denomination signature from a @a cmd.
+ * Obtain wire details from @a cmd.
  *
  * @param cmd command to extract trait from
- * @param selector which coin to pick if @a cmd has multiple on offer
- * @param sig[out] set to a denomination signature over the coin
+ * @param index always (?) zero, as one command sticks
+ *        to one bank account
+ * @param wire_details[out] where to write the wire details.
  * @return #GNUNET_OK on success
  */
 int
-TALER_TESTING_get_trait_denom_sig (const struct TALER_TESTING_Command *cmd,
-                                   const char *selector,
-                                   struct TALER_DenominationSignature **dpk);
+TALER_TESTING_get_trait_wire_details
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   const char **wire_details);
 
 
 /**
- * @param selector
+ * Offer wire details in a trait.
+ *
+ * @param index always (?) zero, as one command sticks
+ *        to one bank account
+ * @param wire_details wire details to offer
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_wire_details
+  (unsigned int index,
+   const char *wire_details);
+
+/**
+ * Obtain a private key from a "peer".  Used e.g. to obtain
+ * a merchant's priv to sign a /track request.
+ *
+ * @param index (tipically zero) which key to return if they
+ *        exist in an array.
+ * @param selector which coin to pick if @a cmd has multiple on
+ * offer
+ * @param priv[out] set to the key coming from @a cmd.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_peer_key
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   const struct GNUNET_CRYPTO_EddsaPrivateKey **priv);
+
+
+/**
+ * @param index (tipically zero) which key to return if they
+ *        exist in an array.
+ * @param priv which object should be returned
+ *
+ * @return the trait, to be put in the traits array of the command
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_denom_sig (const char *selector,
-                                    const struct TALER_DenominationSignature 
*sig);
+TALER_TESTING_make_trait_peer_key
+  (unsigned int index,
+   struct GNUNET_CRYPTO_EddsaPrivateKey *priv);
 
 
+/**
+ * Obtain a transfer subject from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param index always (?) zero, as one command sticks
+ *        to one bank transfer
+ * @param transfer_subject[out] where to write the wire details.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_transfer_subject
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   char **transfer_subject);
 
 
+/**
+ * Offer wire details in a trait.
+ *
+ * @param index always (?) zero, as one command sticks
+ *        to one bank account
+ * @param wire_details wire details to offer
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_transfer_subject
+  (unsigned int index,
+   char *transfer_subject);
 
 
+/**
+ * Obtain a WTID value from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param index which WTID to pick if @a cmd has multiple on
+ *        offer
+ * @param wtid[out] set to the wanted WTID.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_wtid
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   struct TALER_WireTransferIdentifierRawP **wtid);
 
+/**
+ * @param index associate the object with this index
+ * @param wtid which object should be returned
+ *
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_wtid
+  (unsigned int index,
+   struct TALER_WireTransferIdentifierRawP *wtid);
 
 
+/**
+ * Offer amount in a trait.
+ *
+ * @param index which amount is to be picked, in case
+ *        multiple are offered.
+ * @param amount the amount to offer
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_amount
+  (unsigned int index,
+   const char *amount);
 
+/**
+ * Obtain an amount from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param index which amount is to be picked, in case
+ *        multiple are offered.
+ * @param amount[out] where to write the wire details.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_amount
+  (const struct TALER_TESTING_Command *cmd,
+   unsigned int index,
+   const char **amount);
 
 #endif

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



reply via email to

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