gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated (baaa8832 -> da1df35d)


From: gnunet
Subject: [taler-exchange] branch master updated (baaa8832 -> da1df35d)
Date: Sat, 28 Mar 2020 21:57:20 +0100

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

grothoff pushed a change to branch master
in repository exchange.

    from baaa8832 fix leak
     new 2d297a17 skip tests if ports are unavailable
     new 9d9b0e0e split recoup and revoke commands into two files for better 
readability
     new d5351641 implement #6049
     new 82b18857 do return correct timestamp also in fakebank implementation
     new ae9f751a do return fees in history
     new 88957c55 ignore report directories
     new 28492256 ignore generated config file
     new b409d6a1 permit subtraction to result in zero
     new 96c5cd4c do not re-use command label
     new bc57f007 lookup in reverse order also inside of batch
     new da1df35d reserve history amount includes fee, add it

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


Summary of changes:
 src/auditor/.gitignore                             |   1 +
 src/bank-lib/fakebank.c                            |   2 +-
 src/exchange/taler-exchange-aggregator.c           |   2 +-
 src/include/taler_exchange_service.h               |  24 +-
 src/include/taler_testing_lib.h                    | 310 ++++++++++++---------
 src/lib/exchange_api_common.c                      |  10 +-
 src/testing/.gitignore                             |   1 +
 src/testing/Makefile.am                            |   2 +
 src/testing/test_auditor_api.c                     |   7 +-
 src/testing/test_exchange_api.c                    |  31 ++-
 src/testing/test_exchange_api_revocation.c         |  31 ++-
 src/testing/test_taler_exchange_aggregator.c       |   1 -
 src/testing/test_taler_exchange_wirewatch.c        |  20 +-
 .../testing_api_cmd_bank_admin_add_incoming.c      |  37 ++-
 src/testing/testing_api_cmd_exec_closer.c          | 101 ++++++-
 src/testing/testing_api_cmd_recoup.c               | 275 ++++++------------
 src/testing/testing_api_cmd_revoke.c               | 224 +++++++++++++++
 src/testing/testing_api_cmd_status.c               | 230 +++++++++++++--
 src/testing/testing_api_cmd_withdraw.c             |  33 ++-
 src/testing/testing_api_helpers_exchange.c         |  14 +-
 src/testing/testing_api_loop.c                     |  11 +-
 ...e_pub.c => testing_api_trait_reserve_history.c} |  46 +--
 src/testing/testing_api_trait_reserve_pub.c        |   2 +
 23 files changed, 983 insertions(+), 432 deletions(-)
 create mode 100644 src/testing/testing_api_cmd_revoke.c
 copy src/testing/{testing_api_trait_reserve_pub.c => 
testing_api_trait_reserve_history.c} (53%)

diff --git a/src/auditor/.gitignore b/src/auditor/.gitignore
index 021f9e79..7ad6c976 100644
--- a/src/auditor/.gitignore
+++ b/src/auditor/.gitignore
@@ -15,3 +15,4 @@ taler-helper-auditor-deposits
 taler-helper-auditor-reserves
 taler-helper-auditor-wire
 generate-auditor-basedb-prod.conf
+generate-auditor-basedb-revocation.conf
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index dd8d3341..8f4a3610 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -665,7 +665,7 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
                                     (json_int_t) row_id,
                                     "timestamp",
                                     GNUNET_JSON_from_time_abs (
-                                      GNUNET_TIME_UNIT_ZERO_ABS));
+                                      h->transactions_tail->date));
 }
 
 
diff --git a/src/exchange/taler-exchange-aggregator.c 
b/src/exchange/taler-exchange-aggregator.c
index 0ec3b16a..63a72c79 100644
--- a/src/exchange/taler-exchange-aggregator.c
+++ b/src/exchange/taler-exchange-aggregator.c
@@ -312,7 +312,7 @@ refund_by_coin_cb (void *cls,
               "Aggregator subtracts applicable refund of amount %s\n",
               TALER_amount2s (amount_with_fee));
   aux->have_refund = GNUNET_YES;
-  if (GNUNET_OK !=
+  if (GNUNET_SYSERR ==
       TALER_amount_subtract (&aux->total_amount,
                              &aux->total_amount,
                              amount_with_fee))
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 928a808a..e935c919 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -946,7 +946,7 @@ enum TALER_EXCHANGE_ReserveTransactionType
   /**
    * Deposit into the reserve.
    */
-  TALER_EXCHANGE_RTT_DEPOSIT,
+  TALER_EXCHANGE_RTT_CREDIT,
 
   /**
    * Withdrawal from the reserve.
@@ -990,7 +990,7 @@ struct TALER_EXCHANGE_ReserveHistory
 
     /**
      * Information about a deposit that filled this reserve.
-     * @e type is #TALER_EXCHANGE_RTT_DEPOSIT.
+     * @e type is #TALER_EXCHANGE_RTT_CREDIT.
      */
     struct
     {
@@ -1017,10 +1017,21 @@ struct TALER_EXCHANGE_ReserveHistory
     } in_details;
 
     /**
-     * Signature authorizing the withdrawal for outgoing transaction.
+     * Information about withdraw operation.
      * @e type is #TALER_EXCHANGE_RTT_WITHDRAWAL.
      */
-    json_t *out_authorization_sig;
+    struct
+    {
+      /**
+       * Signature authorizing the withdrawal for outgoing transaction.
+       */
+      json_t *out_authorization_sig;
+
+      /**
+       * Fee that was charged for the withdrawal.
+       */
+      struct TALER_Amount fee;
+    } withdraw;
 
     /**
      * Information provided if the reserve was filled via /recoup.
@@ -1084,6 +1095,11 @@ struct TALER_EXCHANGE_ReserveHistory
        */
       struct GNUNET_TIME_Absolute timestamp;
 
+      /**
+       * Fee that was charged for the closing.
+       */
+      struct TALER_Amount fee;
+
     } close_details;
 
   } details;
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index 9e473f75..4fae085d 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -548,8 +548,8 @@ TALER_TESTING_interpreter_get_context (struct 
TALER_TESTING_Interpreter *is);
  * @return the 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);
 
 
 /**
@@ -730,9 +730,9 @@ struct TALER_TESTING_SetupContext
  * @return #GNUNET_OK if no errors occurred.
  */
 int
-TALER_TESTING_setup_with_exchange_cfg (void *cls,
-                                       const struct
-                                       GNUNET_CONFIGURATION_Handle *cfg);
+TALER_TESTING_setup_with_exchange_cfg (
+  void *cls,
+  const struct GNUNET_CONFIGURATION_Handle *cfg);
 
 
 /**
@@ -766,10 +766,9 @@ TALER_TESTING_setup_with_exchange (TALER_TESTING_Main 
main_cb,
  * @return #GNUNET_OK if no errors occurred.
  */
 int
-TALER_TESTING_setup_with_auditor_and_exchange_cfg (void *cls,
-                                                   const struct
-                                                   GNUNET_CONFIGURATION_Handle 
*
-                                                   cfg);
+TALER_TESTING_setup_with_auditor_and_exchange_cfg (
+  void *cls,
+  const struct GNUNET_CONFIGURATION_Handle *cfg);
 
 
 /**
@@ -866,11 +865,11 @@ TALER_TESTING_has_in_name (const char *prog,
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_bank_credits (const char *label,
-                                const struct
-                                TALER_BANK_AuthenticationData *auth,
-                                const char *start_row_reference,
-                                long long num_results);
+TALER_TESTING_cmd_bank_credits (
+  const char *label,
+  const struct TALER_BANK_AuthenticationData *auth,
+  const char *start_row_reference,
+  long long num_results);
 
 
 /**
@@ -968,8 +967,9 @@ TALER_TESTING_cmd_deposit_confirmation (const char *label,
  * @return the command with retries enabled
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_deposit_confirmation_with_retry (struct TALER_TESTING_Command
-                                                   cmd);
+TALER_TESTING_cmd_deposit_confirmation_with_retry (
+  struct TALER_TESTING_Command
+  cmd);
 
 
 /**
@@ -1024,11 +1024,11 @@ TALER_TESTING_cmd_exchanges_with_retry (struct 
TALER_TESTING_Command cmd);
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_admin_add_incoming (const char *label,
-                                      const char *amount,
-                                      const struct
-                                      TALER_BANK_AuthenticationData *auth,
-                                      const char *payto_debit_account);
+TALER_TESTING_cmd_admin_add_incoming (
+  const char *label,
+  const char *amount,
+  const struct TALER_BANK_AuthenticationData *auth,
+  const char *payto_debit_account);
 
 
 /**
@@ -1046,13 +1046,12 @@ TALER_TESTING_cmd_admin_add_incoming (const char *label,
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_admin_add_incoming_with_ref (const char *label,
-                                               const char *amount,
-                                               const struct
-                                               TALER_BANK_AuthenticationData *
-                                               auth,
-                                               const char *payto_debit_account,
-                                               const char *ref);
+TALER_TESTING_cmd_admin_add_incoming_with_ref (
+  const char *label,
+  const char *amount,
+  const struct TALER_BANK_AuthenticationData *auth,
+  const char *payto_debit_account,
+  const char *ref);
 
 
 /**
@@ -1074,15 +1073,13 @@ TALER_TESTING_cmd_admin_add_incoming_with_ref (const 
char *label,
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_admin_add_incoming_with_instance (const char *label,
-                                                    const char *amount,
-                                                    const struct
-                                                    
TALER_BANK_AuthenticationData
-                                                    *auth,
-                                                    const char *
-                                                    payto_debit_account,
-                                                    const char *instance,
-                                                    const char 
*config_filename);
+TALER_TESTING_cmd_admin_add_incoming_with_instance (
+  const char *label,
+  const char *amount,
+  const struct TALER_BANK_AuthenticationData *auth,
+  const char *payto_debit_account,
+  const char *instance,
+  const char *config_filename);
 
 
 /**
@@ -1123,16 +1120,27 @@ TALER_TESTING_cmd_exec_aggregator (const char *label,
 
 
 /**
- * Make a "closer" CMD.
+ * Make a "closer" CMD.  Note that it is right now not supported to run the
+ * closer to close multiple reserves in combination with a subsequent reserve
+ * status call, as we cannot generate the traits necessary for multiple closed
+ * reserves.  You can work around this by using multiple closer commands, one
+ * per reserve that is being closed.
  *
  * @param label command label.
  * @param config_filename configuration file for the
  *                        closer to use.
+ * @param expected_amount amount we expect to see wired from a @a 
expected_reserve_ref
+ * @param expected_fee closing fee we expect to see
+ * @param expected_reserve_ref reference to a reserve we expect the closer to 
drain;
+ *          NULL if we do not expect the closer to do anything
  * @return the command.
  */
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_exec_closer (const char *label,
-                               const char *config_filename);
+                               const char *config_filename,
+                               const char *expected_amount,
+                               const char *expected_fee,
+                               const char *expected_reserve_ref);
 
 
 /**
@@ -1661,13 +1669,15 @@ TALER_TESTING_cmd_refund (const char *label,
  *        the index of the coin using "$LABEL#$INDEX" syntax.
  *        Here, $INDEX must be a non-negative number.
  * @param melt_reference NULL if coin was not refreshed, otherwise label of 
the melt operation
+ * @param amount how much do we expect to recoup, NULL for nothing
  * @return the command.
  */
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_recoup (const char *label,
                           unsigned int expected_response_code,
                           const char *coin_reference,
-                          const char *melt_reference);
+                          const char *melt_reference,
+                          const char *amount);
 
 
 /**
@@ -2006,9 +2016,9 @@ TALER_TESTING_make_trait_bank_row (const uint64_t *row);
  * @return the trait.
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_reserve_priv (unsigned int index,
-                                       const struct
-                                       TALER_ReservePrivateKeyP *reserve_priv);
+TALER_TESTING_make_trait_reserve_priv (
+  unsigned int index,
+  const struct TALER_ReservePrivateKeyP *reserve_priv);
 
 
 /**
@@ -2020,10 +2030,10 @@ TALER_TESTING_make_trait_reserve_priv (unsigned int 
index,
  * @return #GNUNET_OK on success.
  */
 int
-TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd,
-                                      unsigned int index,
-                                      const struct
-                                      TALER_ReservePrivateKeyP **reserve_priv);
+TALER_TESTING_get_trait_reserve_priv (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct TALER_ReservePrivateKeyP **reserve_priv);
 
 
 /**
@@ -2034,9 +2044,9 @@ TALER_TESTING_get_trait_reserve_priv (const struct 
TALER_TESTING_Command *cmd,
  * @return the trait.
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_reserve_pub (unsigned int index,
-                                      const struct
-                                      TALER_ReservePublicKeyP *reserve_pub);
+TALER_TESTING_make_trait_reserve_pub (
+  unsigned int index,
+  const struct TALER_ReservePublicKeyP *reserve_pub);
 
 
 /**
@@ -2048,10 +2058,38 @@ TALER_TESTING_make_trait_reserve_pub (unsigned int 
index,
  * @return #GNUNET_OK on success.
  */
 int
-TALER_TESTING_get_trait_reserve_pub (const struct TALER_TESTING_Command *cmd,
-                                     unsigned int index,
-                                     const struct
-                                     TALER_ReservePublicKeyP **reserve_pub);
+TALER_TESTING_get_trait_reserve_pub (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct TALER_ReservePublicKeyP **reserve_pub);
+
+
+/**
+ * Offer a reserve history entry.
+ *
+ * @param index reserve pubs's index number.
+ * @param rh reserve history entry to offer.
+ * @return the trait.
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_reserve_history (
+  unsigned int index,
+  const struct TALER_EXCHANGE_ReserveHistory *rh);
+
+
+/**
+ * Obtain a reserve history entry from a @a cmd.
+ *
+ * @param cmd command to extract the reserve history from.
+ * @param index reserve history's index number.
+ * @param[out] rhp set to the reserve history.
+ * @return #GNUNET_OK on success.
+ */
+int
+TALER_TESTING_get_trait_reserve_history (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct TALER_EXCHANGE_ReserveHistory **rhp);
 
 
 /**
@@ -2063,9 +2101,9 @@ TALER_TESTING_get_trait_reserve_pub (const struct 
TALER_TESTING_Command *cmd,
  * @return the trait.
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_exchange_sig (unsigned int index,
-                                       const struct
-                                       TALER_ExchangeSignatureP *exchange_sig);
+TALER_TESTING_make_trait_exchange_sig (
+  unsigned int index,
+  const struct TALER_ExchangeSignatureP *exchange_sig);
 
 
 /**
@@ -2077,10 +2115,10 @@ TALER_TESTING_make_trait_exchange_sig (unsigned int 
index,
  * @return #GNUNET_OK on success.
  */
 int
-TALER_TESTING_get_trait_exchange_sig (const struct TALER_TESTING_Command *cmd,
-                                      unsigned int index,
-                                      const struct
-                                      TALER_ExchangeSignatureP **exchange_sig);
+TALER_TESTING_get_trait_exchange_sig (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct TALER_ExchangeSignatureP **exchange_sig);
 
 
 /**
@@ -2092,9 +2130,9 @@ TALER_TESTING_get_trait_exchange_sig (const struct 
TALER_TESTING_Command *cmd,
  * @return the trait.
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_exchange_pub (unsigned int index,
-                                       const struct
-                                       TALER_ExchangePublicKeyP *exchange_pub);
+TALER_TESTING_make_trait_exchange_pub (
+  unsigned int index,
+  const struct TALER_ExchangePublicKeyP *exchange_pub);
 
 
 /**
@@ -2106,10 +2144,10 @@ TALER_TESTING_make_trait_exchange_pub (unsigned int 
index,
  * @return #GNUNET_OK on success.
  */
 int
-TALER_TESTING_get_trait_exchange_pub (const struct TALER_TESTING_Command *cmd,
-                                      unsigned int index,
-                                      const struct
-                                      TALER_ExchangePublicKeyP **exchange_pub);
+TALER_TESTING_get_trait_exchange_pub (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct TALER_ExchangePublicKeyP **exchange_pub);
 
 
 /**
@@ -2149,9 +2187,9 @@ TALER_TESTING_make_trait_process (unsigned int index,
  * @return the trait.
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_coin_priv (unsigned int index,
-                                    const struct
-                                    TALER_CoinSpendPrivateKeyP *coin_priv);
+TALER_TESTING_make_trait_coin_priv (
+  unsigned int index,
+  const struct TALER_CoinSpendPrivateKeyP *coin_priv);
 
 /**
  * Obtain a coin private key from a @a cmd.
@@ -2162,10 +2200,10 @@ TALER_TESTING_make_trait_coin_priv (unsigned int index,
  * @return #GNUNET_OK on success.
  */
 int
-TALER_TESTING_get_trait_coin_priv (const struct TALER_TESTING_Command *cmd,
-                                   unsigned int index,
-                                   const struct
-                                   TALER_CoinSpendPrivateKeyP **coin_priv);
+TALER_TESTING_get_trait_coin_priv (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct TALER_CoinSpendPrivateKeyP **coin_priv);
 
 
 /**
@@ -2176,10 +2214,9 @@ TALER_TESTING_get_trait_coin_priv (const struct 
TALER_TESTING_Command *cmd,
  * @return the trait.
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_blinding_key (unsigned int index,
-                                       const struct
-                                       TALER_DenominationBlindingKeyP *
-                                       blinding_key);
+TALER_TESTING_make_trait_blinding_key (
+  unsigned int index,
+  const struct TALER_DenominationBlindingKeyP *blinding_key);
 
 
 /**
@@ -2191,11 +2228,10 @@ TALER_TESTING_make_trait_blinding_key (unsigned int 
index,
  * @return #GNUNET_OK on success.
  */
 int
-TALER_TESTING_get_trait_blinding_key (const struct TALER_TESTING_Command *cmd,
-                                      unsigned int index,
-                                      const struct
-                                      TALER_DenominationBlindingKeyP **
-                                      blinding_key);
+TALER_TESTING_get_trait_blinding_key (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct TALER_DenominationBlindingKeyP **blinding_key);
 
 
 /**
@@ -2206,9 +2242,9 @@ TALER_TESTING_get_trait_blinding_key (const struct 
TALER_TESTING_Command *cmd,
  * @return the trait.
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_denom_pub (unsigned int index,
-                                    const struct
-                                    TALER_EXCHANGE_DenomPublicKey *dpk);
+TALER_TESTING_make_trait_denom_pub (
+  unsigned int index,
+  const struct TALER_EXCHANGE_DenomPublicKey *dpk);
 
 
 /**
@@ -2220,10 +2256,10 @@ TALER_TESTING_make_trait_denom_pub (unsigned int index,
  * @return #GNUNET_OK on success.
  */
 int
-TALER_TESTING_get_trait_denom_pub (const struct TALER_TESTING_Command *cmd,
-                                   unsigned int index,
-                                   const struct
-                                   TALER_EXCHANGE_DenomPublicKey **dpk);
+TALER_TESTING_get_trait_denom_pub (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct TALER_EXCHANGE_DenomPublicKey **dpk);
 
 
 /**
@@ -2235,10 +2271,10 @@ TALER_TESTING_get_trait_denom_pub (const struct 
TALER_TESTING_Command *cmd,
  * @return #GNUNET_OK on success.
  */
 int
-TALER_TESTING_get_trait_denom_sig (const struct TALER_TESTING_Command *cmd,
-                                   unsigned int index,
-                                   const struct
-                                   TALER_DenominationSignature **dpk);
+TALER_TESTING_get_trait_denom_sig (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct TALER_DenominationSignature **dpk);
 
 
 /**
@@ -2250,9 +2286,9 @@ TALER_TESTING_get_trait_denom_sig (const struct 
TALER_TESTING_Command *cmd,
  * @return the trait.
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_denom_sig (unsigned int index,
-                                    const struct
-                                    TALER_DenominationSignature *sig);
+TALER_TESTING_make_trait_denom_sig (
+  unsigned int index,
+  const struct TALER_DenominationSignature *sig);
 
 
 /**
@@ -2323,9 +2359,9 @@ struct TALER_TESTING_FreshCoinData;
  * @return the trait,
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_fresh_coins (unsigned int index,
-                                      const struct TALER_TESTING_FreshCoinData 
*
-                                      fresh_coins);
+TALER_TESTING_make_trait_fresh_coins (
+  unsigned int index,
+  const struct TALER_TESTING_FreshCoinData *fresh_coins);
 
 
 /**
@@ -2338,10 +2374,10 @@ TALER_TESTING_make_trait_fresh_coins (unsigned int 
index,
  * @return #GNUNET_OK on success.
  */
 int
-TALER_TESTING_get_trait_fresh_coins (const struct TALER_TESTING_Command *cmd,
-                                     unsigned int index,
-                                     const struct
-                                     TALER_TESTING_FreshCoinData 
**fresh_coins);
+TALER_TESTING_get_trait_fresh_coins (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct TALER_TESTING_FreshCoinData **fresh_coins);
 
 
 /**
@@ -2440,10 +2476,10 @@ TALER_TESTING_make_trait_exchange_keys (unsigned int 
index,
  * @return #GNUNET_OK on success.
  */
 int
-TALER_TESTING_get_trait_merchant_priv (const struct TALER_TESTING_Command *cmd,
-                                       unsigned int index,
-                                       const struct
-                                       TALER_MerchantPrivateKeyP **priv);
+TALER_TESTING_get_trait_merchant_priv (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct TALER_MerchantPrivateKeyP **priv);
 
 
 /**
@@ -2456,9 +2492,9 @@ TALER_TESTING_get_trait_merchant_priv (const struct 
TALER_TESTING_Command *cmd,
  * @return the trait.
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_merchant_priv (unsigned int index,
-                                        const struct
-                                        TALER_MerchantPrivateKeyP *priv);
+TALER_TESTING_make_trait_merchant_priv (
+  unsigned int index,
+  const struct TALER_MerchantPrivateKeyP *priv);
 
 
 /**
@@ -2472,10 +2508,10 @@ TALER_TESTING_make_trait_merchant_priv (unsigned int 
index,
  * @return #GNUNET_OK on success.
  */
 int
-TALER_TESTING_get_trait_merchant_pub (const struct TALER_TESTING_Command *cmd,
-                                      unsigned int index,
-                                      const struct
-                                      TALER_MerchantPublicKeyP **pub);
+TALER_TESTING_get_trait_merchant_pub (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct TALER_MerchantPublicKeyP **pub);
 
 
 /**
@@ -2488,9 +2524,9 @@ TALER_TESTING_get_trait_merchant_pub (const struct 
TALER_TESTING_Command *cmd,
  * @return the trait.
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_merchant_pub (unsigned int index,
-                                       const struct
-                                       TALER_MerchantPublicKeyP *pub);
+TALER_TESTING_make_trait_merchant_pub (
+  unsigned int index,
+  const struct TALER_MerchantPublicKeyP *pub);
 
 
 /**
@@ -2504,10 +2540,10 @@ TALER_TESTING_make_trait_merchant_pub (unsigned int 
index,
  * @return #GNUNET_OK on success.
  */
 int
-TALER_TESTING_get_trait_string (const struct
-                                TALER_TESTING_Command *cmd,
-                                unsigned int index,
-                                const char **s);
+TALER_TESTING_get_trait_string (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const char **s);
 
 
 /**
@@ -2533,10 +2569,10 @@ TALER_TESTING_make_trait_string (unsigned int index,
  * @return #GNUNET_OK on success
  */
 int
-TALER_TESTING_get_trait_wtid (const struct TALER_TESTING_Command *cmd,
-                              unsigned int index,
-                              const struct
-                              TALER_WireTransferIdentifierRawP **wtid);
+TALER_TESTING_get_trait_wtid (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct TALER_WireTransferIdentifierRawP **wtid);
 
 
 /**
@@ -2547,9 +2583,9 @@ TALER_TESTING_get_trait_wtid (const struct 
TALER_TESTING_Command *cmd,
  * @return the trait.
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_wtid (unsigned int index,
-                               const struct
-                               TALER_WireTransferIdentifierRawP *wtid);
+TALER_TESTING_make_trait_wtid (
+  unsigned int index,
+  const struct TALER_WireTransferIdentifierRawP *wtid);
 
 
 /**
@@ -2752,10 +2788,10 @@ TALER_TESTING_get_trait_cmd (const struct 
TALER_TESTING_Command *cmd,
  * @return #GNUNET_OK on success
  */
 int
-TALER_TESTING_get_trait_absolute_time (const struct TALER_TESTING_Command *cmd,
-                                       unsigned int index,
-                                       const struct
-                                       GNUNET_TIME_Absolute **time);
+TALER_TESTING_get_trait_absolute_time (
+  const struct TALER_TESTING_Command *cmd,
+  unsigned int index,
+  const struct GNUNET_TIME_Absolute **time);
 
 
 /**
@@ -2766,8 +2802,8 @@ TALER_TESTING_get_trait_absolute_time (const struct 
TALER_TESTING_Command *cmd,
  * @return the trait.
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_absolute_time (unsigned int index,
-                                        const struct
-                                        GNUNET_TIME_Absolute *time);
+TALER_TESTING_make_trait_absolute_time (
+  unsigned int index,
+  const struct GNUNET_TIME_Absolute *time);
 
 #endif
diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c
index 70d1432f..2748b8fc 100644
--- a/src/lib/exchange_api_common.c
+++ b/src/lib/exchange_api_common.c
@@ -109,7 +109,7 @@ TALER_EXCHANGE_parse_reserve_history (
         GNUNET_JSON_spec_end ()
       };
 
-      rhistory[off].type = TALER_EXCHANGE_RTT_DEPOSIT;
+      rhistory[off].type = TALER_EXCHANGE_RTT_CREDIT;
       if (GNUNET_OK !=
           TALER_amount_add (&total_in,
                             &total_in,
@@ -201,8 +201,9 @@ TALER_EXCHANGE_parse_reserve_history (
           GNUNET_JSON_parse_free (withdraw_spec);
           return GNUNET_SYSERR;
         }
+        rhistory[off].details.withdraw.fee = fee;
       }
-      rhistory[off].details.out_authorization_sig
+      rhistory[off].details.withdraw.out_authorization_sig
         = json_object_get (transaction,
                            "signature");
       /* Check check that the same withdraw transaction
@@ -355,7 +356,8 @@ TALER_EXCHANGE_parse_reserve_history (
       rcc.reserve_pub = *reserve_pub;
       timestamp = GNUNET_TIME_absolute_ntoh (rcc.timestamp);
       rhistory[off].details.close_details.timestamp = timestamp;
-
+      TALER_amount_ntoh (&rhistory[off].details.close_details.fee,
+                         &rcc.closing_fee);
       key_state = TALER_EXCHANGE_get_keys (exchange);
       if (GNUNET_OK !=
           TALER_EXCHANGE_test_signing_key (key_state,
@@ -424,7 +426,7 @@ TALER_EXCHANGE_free_reserve_history (
   {
     switch (rhistory[i].type)
     {
-    case TALER_EXCHANGE_RTT_DEPOSIT:
+    case TALER_EXCHANGE_RTT_CREDIT:
       GNUNET_free_non_null (rhistory[i].details.in_details.wire_reference);
       GNUNET_free_non_null (rhistory[i].details.in_details.sender_url);
       break;
diff --git a/src/testing/.gitignore b/src/testing/.gitignore
index 17a848cc..216c632e 100644
--- a/src/testing/.gitignore
+++ b/src/testing/.gitignore
@@ -6,3 +6,4 @@ test_bank_api_with_pybank_twisted
 test_taler_exchange_aggregator-postgres
 test_taler_exchange_wirewatch-postgres
 test_exchange_api_revocation
+report*
diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am
index 7b36a3ae..e4e25d32 100644
--- a/src/testing/Makefile.am
+++ b/src/testing/Makefile.am
@@ -59,6 +59,7 @@ libtalertesting_la_SOURCES = \
   testing_api_cmd_recoup.c \
   testing_api_cmd_refund.c \
   testing_api_cmd_refresh.c \
+  testing_api_cmd_revoke.c \
   testing_api_cmd_serialize_keys.c \
   testing_api_cmd_signal.c \
   testing_api_cmd_sleep.c \
@@ -88,6 +89,7 @@ libtalertesting_la_SOURCES = \
   testing_api_trait_merchant_key.c \
   testing_api_trait_number.c \
   testing_api_trait_process.c \
+  testing_api_trait_reserve_history.c \
   testing_api_trait_reserve_pub.c \
   testing_api_trait_reserve_priv.c \
   testing_api_trait_string.c \
diff --git a/src/testing/test_auditor_api.c b/src/testing/test_auditor_api.c
index 46d9bea3..be1654c6 100644
--- a/src/testing/test_auditor_api.c
+++ b/src/testing/test_auditor_api.c
@@ -71,7 +71,6 @@ static struct TALER_TESTING_BankConfiguration bc;
  */
 #define CMD_EXEC_AGGREGATOR(label) \
   TALER_TESTING_cmd_exec_aggregator (label, CONFIG_FILE), \
-  TALER_TESTING_cmd_exec_closer (label, CONFIG_FILE), \
   TALER_TESTING_cmd_exec_transfer (label, CONFIG_FILE)
 
 /**
@@ -401,7 +400,8 @@ run (void *cls,
     TALER_TESTING_cmd_recoup ("recoup-1",
                               MHD_HTTP_OK,
                               "recoup-withdraw-coin-1",
-                              NULL),
+                              NULL,
+                              "EUR:5"),
     /**
      * Re-withdraw from this reserve
      */
@@ -460,7 +460,8 @@ run (void *cls,
     TALER_TESTING_cmd_recoup ("recoup-2",
                               MHD_HTTP_OK,
                               "recoup-withdraw-coin-2a",
-                              NULL),
+                              NULL,
+                              "EUR:0.5"),
     TALER_TESTING_cmd_end ()
   };
 
diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c
index 7832e736..4c56925d 100644
--- a/src/testing/test_exchange_api.c
+++ b/src/testing/test_exchange_api.c
@@ -72,7 +72,6 @@ static struct TALER_TESTING_BankConfiguration bc;
  */
 #define CMD_EXEC_AGGREGATOR(label) \
   TALER_TESTING_cmd_exec_aggregator (label "-aggregator", CONFIG_FILE), \
-  TALER_TESTING_cmd_exec_closer (label "-closer", CONFIG_FILE), \
   TALER_TESTING_cmd_exec_transfer (label "-transfer", CONFIG_FILE)
 
 
@@ -628,11 +627,12 @@ run (void *cls,
      */
     CMD_TRANSFER_TO_EXCHANGE ("recoup-create-reserve-1",
                               "EUR:5.01"),
-    TALER_TESTING_cmd_check_bank_admin_transfer ("recoup-create-reserve-1",
-                                                 "EUR:5.01",
-                                                 bc.user42_payto,
-                                                 bc.exchange_payto,
-                                                 "recoup-create-reserve-1"),
+    TALER_TESTING_cmd_check_bank_admin_transfer (
+      "recoup-create-reserve-1-check",
+      "EUR:5.01",
+      bc.user42_payto,
+      bc.exchange_payto,
+      "recoup-create-reserve-1"),
     /**
      * Run wire-watch to trigger the reserve creation.
      */
@@ -651,7 +651,8 @@ run (void *cls,
     TALER_TESTING_cmd_recoup ("recoup-1",
                               MHD_HTTP_OK,
                               "recoup-withdraw-coin-1",
-                              NULL),
+                              NULL,
+                              "EUR:5"),
     /* Check the money is back with the reserve */
     TALER_TESTING_cmd_status ("recoup-reserve-status-1",
                               "recoup-create-reserve-1",
@@ -674,7 +675,6 @@ run (void *cls,
                               "recoup-create-reserve-1",
                               "EUR:3.99",
                               MHD_HTTP_OK),
-
     /* These commands should close the reserve because
      * the aggregator is given a config file that ovverrides
      * the reserve expiration time (making it now-ish) */
@@ -687,9 +687,11 @@ run (void *cls,
                                                  "short-lived-reserve"),
     TALER_TESTING_cmd_exec_wirewatch ("short-lived-aggregation",
                                       CONFIG_FILE_EXPIRE_RESERVE_NOW),
-
     TALER_TESTING_cmd_exec_closer ("close-reserves",
-                                   CONFIG_FILE_EXPIRE_RESERVE_NOW),
+                                   CONFIG_FILE_EXPIRE_RESERVE_NOW,
+                                   "EUR:5",
+                                   "EUR:0.01",
+                                   "short-lived-reserve"),
     TALER_TESTING_cmd_exec_transfer ("close-reserves-transfer",
                                      CONFIG_FILE_EXPIRE_RESERVE_NOW),
 
@@ -745,11 +747,13 @@ run (void *cls,
     TALER_TESTING_cmd_recoup ("recoup-2",
                               MHD_HTTP_OK,
                               "recoup-withdraw-coin-2a",
-                              NULL),
+                              NULL,
+                              "EUR:0.5"),
     /* Idempotency of recoup (withdrawal variant) */
     TALER_TESTING_cmd_recoup ("recoup-2b",
                               MHD_HTTP_OK,
                               "recoup-withdraw-coin-2a",
+                              NULL,
                               NULL),
     TALER_TESTING_cmd_deposit ("recoup-deposit-revoked",
                                "recoup-withdraw-coin-2b",
@@ -809,7 +813,10 @@ run (void *cls,
                                           CONFIG_FILE_EXPIRE_RESERVE_NOW);
     reserve_open_close[(i * RESERVE_OPEN_CLOSE_CHUNK) + 2]
       = TALER_TESTING_cmd_exec_closer ("reserve-open-close-aggregation",
-                                       CONFIG_FILE_EXPIRE_RESERVE_NOW);
+                                       CONFIG_FILE_EXPIRE_RESERVE_NOW,
+                                       "EUR:19.99",
+                                       "EUR:0.01",
+                                       "reserve-open-close-key");
     reserve_open_close[(i * RESERVE_OPEN_CLOSE_CHUNK) + 3]
       = TALER_TESTING_cmd_status ("reserve-open-close-status",
                                   "reserve-open-close-key",
diff --git a/src/testing/test_exchange_api_revocation.c 
b/src/testing/test_exchange_api_revocation.c
index f91d8b54..0f3a8910 100644
--- a/src/testing/test_exchange_api_revocation.c
+++ b/src/testing/test_exchange_api_revocation.c
@@ -126,7 +126,8 @@ run (void *cls,
     TALER_TESTING_cmd_recoup ("recoup-not-allowed",
                               MHD_HTTP_NOT_FOUND,
                               "refresh-reveal-1#0",
-                              "refresh-melt-1"),
+                              "refresh-melt-1",
+                              NULL),
     /* Make refreshed coin invalid */
     TALER_TESTING_cmd_revoke ("revoke-2-EUR:5",
                               MHD_HTTP_OK,
@@ -141,37 +142,45 @@ run (void *cls,
     TALER_TESTING_cmd_recoup ("recoup-fully-spent",
                               MHD_HTTP_CONFLICT,
                               "withdraw-revocation-coin-2",
+                              NULL,
                               NULL),
     /* Refund coin to original coin */
     TALER_TESTING_cmd_recoup ("recoup-1a",
                               MHD_HTTP_OK,
                               "refresh-reveal-1#0",
-                              "refresh-melt-1"),
+                              "refresh-melt-1",
+                              NULL),
     TALER_TESTING_cmd_recoup ("recoup-1b",
                               MHD_HTTP_OK,
                               "refresh-reveal-1#1",
-                              "refresh-melt-1"),
+                              "refresh-melt-1",
+                              NULL),
     TALER_TESTING_cmd_recoup ("recoup-1c",
                               MHD_HTTP_OK,
                               "refresh-reveal-1#2",
-                              "refresh-melt-1"),
+                              "refresh-melt-1",
+                              NULL),
     /* Repeat recoup to test idempotency */
     TALER_TESTING_cmd_recoup ("recoup-1c",
                               MHD_HTTP_OK,
                               "refresh-reveal-1#2",
-                              "refresh-melt-1"),
+                              "refresh-melt-1",
+                              NULL),
     TALER_TESTING_cmd_recoup ("recoup-1c",
                               MHD_HTTP_OK,
                               "refresh-reveal-1#2",
-                              "refresh-melt-1"),
+                              "refresh-melt-1",
+                              NULL),
     TALER_TESTING_cmd_recoup ("recoup-1c",
                               MHD_HTTP_OK,
                               "refresh-reveal-1#2",
-                              "refresh-melt-1"),
+                              "refresh-melt-1",
+                              NULL),
     TALER_TESTING_cmd_recoup ("recoup-1c",
                               MHD_HTTP_OK,
                               "refresh-reveal-1#2",
-                              "refresh-melt-1"),
+                              "refresh-melt-1",
+                              NULL),
     /* Now we have EUR:3.83 EUR back after 3x EUR:1 in recoups */
     /* Melt original coin AGAIN, but only create one 0.1 EUR coin;
        This costs EUR:0.03 in refresh and EUR:01 in withdraw fees,
@@ -201,13 +210,15 @@ run (void *cls,
     TALER_TESTING_cmd_recoup ("recoup-2",
                               MHD_HTTP_OK,
                               "refresh-reveal-2",
-                              "refresh-melt-2"),
+                              "refresh-melt-2",
+                              NULL),
     /* Due to recoup, original coin is now at EUR:3.79 */
     /* Refund original (now zombie) coin to reserve */
     TALER_TESTING_cmd_recoup ("recoup-3",
                               MHD_HTTP_OK,
                               "withdraw-revocation-coin-1",
-                              NULL),
+                              NULL,
+                              "EUR:3.79"),
     /* Check the money is back with the reserve */
     TALER_TESTING_cmd_status ("recoup-reserve-status-1",
                               "create-reserve-1",
diff --git a/src/testing/test_taler_exchange_aggregator.c 
b/src/testing/test_taler_exchange_aggregator.c
index 7f03d8db..eaa621cf 100644
--- a/src/testing/test_taler_exchange_aggregator.c
+++ b/src/testing/test_taler_exchange_aggregator.c
@@ -67,7 +67,6 @@ static char *config_filename;
  */
 #define CMD_EXEC_AGGREGATOR(label, cfg_fn)                                 \
   TALER_TESTING_cmd_exec_aggregator (label "-aggregator", cfg_fn), \
-  TALER_TESTING_cmd_exec_closer (label "-closer", cfg_fn),   \
   TALER_TESTING_cmd_exec_transfer (label "-transfer", cfg_fn)
 
 
diff --git a/src/testing/test_taler_exchange_wirewatch.c 
b/src/testing/test_taler_exchange_wirewatch.c
index 6d7a144e..fbcf742f 100644
--- a/src/testing/test_taler_exchange_wirewatch.c
+++ b/src/testing/test_taler_exchange_wirewatch.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  (C) 2016, 2017, 2018 Taler Systems SA
+  (C) 2016-2020 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
@@ -57,7 +57,6 @@ static char *config_filename;
  */
 #define CMD_EXEC_AGGREGATOR(label) \
   TALER_TESTING_cmd_exec_aggregator (label "-aggregator", config_filename), \
-  TALER_TESTING_cmd_exec_closer (label "-closer", config_filename),   \
   TALER_TESTING_cmd_exec_transfer (label "-transfer", config_filename)
 
 
@@ -101,12 +100,25 @@ run (void *cls,
       bc.exchange_payto,                                            // credit
       "run-transfer-good-to-exchange"),
 
-    CMD_EXEC_AGGREGATOR ("run-aggregator-non-expired-reserve"),
+    TALER_TESTING_cmd_exec_closer ("run-closer-non-expired-reserve",
+                                   config_filename,
+                                   NULL,
+                                   NULL,
+                                   NULL),
+    TALER_TESTING_cmd_exec_transfer ("do-idle-transfer", config_filename),
 
     TALER_TESTING_cmd_check_bank_empty ("expect-empty-transactions-1"),
     TALER_TESTING_cmd_sleep ("wait (5s)",
                              5),
-    CMD_EXEC_AGGREGATOR ("run-aggregator-on-expired-reserve"),
+    TALER_TESTING_cmd_exec_closer ("run-closer-expired-reserve",
+                                   config_filename,
+                                   "EUR:4.99",
+                                   "EUR:0.01",
+                                   "run-transfer-good-to-exchange"),
+    TALER_TESTING_cmd_exec_transfer ("do-closing-transfer",
+                                     config_filename),
+
+    CMD_EXEC_AGGREGATOR ("run-closer-on-expired-reserve"),
     TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-1",
                                            ec.exchange_url,
                                            "EUR:4.99",
diff --git a/src/testing/testing_api_cmd_bank_admin_add_incoming.c 
b/src/testing/testing_api_cmd_bank_admin_add_incoming.c
index 30ba83e2..04e6839d 100644
--- a/src/testing/testing_api_cmd_bank_admin_add_incoming.c
+++ b/src/testing/testing_api_cmd_bank_admin_add_incoming.c
@@ -96,11 +96,24 @@ struct AdminAddIncomingState
    */
   struct TALER_TESTING_Interpreter *is;
 
+  /**
+   * Reserve history entry that corresponds to this operation.
+   * Will be of type #TALER_EXCHANGE_RTT_CREDIT.  Note that
+   * the "sender_url" field is set to a 'const char *' and
+   * MUST NOT be free()'ed.
+   */
+  struct TALER_EXCHANGE_ReserveHistory reserve_history;
+
   /**
    * Set to the wire transfer's unique ID.
    */
   uint64_t serial_id;
 
+  /**
+   * Set to the wire transfer's row ID in network byte order.
+   */
+  uint64_t row_id_nbo;
+
   /**
    * Timestamp of the transaction (as returned from the bank).
    */
@@ -196,6 +209,11 @@ confirmation_cb (void *cls,
   struct TALER_TESTING_Interpreter *is = fts->is;
 
   (void) json;
+  fts->row_id_nbo = GNUNET_htonll (serial_id);
+  fts->reserve_history.details.in_details.timestamp = timestamp;
+  fts->reserve_history.details.in_details.wire_reference = &fts->row_id_nbo;
+  fts->reserve_history.details.in_details.wire_reference_size
+    = sizeof (fts->row_id_nbo);
   fts->aih = NULL;
   if (MHD_HTTP_OK != http_status)
   {
@@ -322,11 +340,10 @@ admin_add_incoming_run (void *cls,
       GNUNET_free (keys);
       if (NULL == priv)
       {
-        GNUNET_log_config_invalid
-          (GNUNET_ERROR_TYPE_ERROR,
-          section,
-          "TIP_RESERVE_PRIV_FILENAME",
-          "Failed to read private key");
+        GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                                   section,
+                                   "TIP_RESERVE_PRIV_FILENAME",
+                                   "Failed to read private key");
         GNUNET_free (section);
         TALER_TESTING_interpreter_fail (is);
         return;
@@ -349,10 +366,14 @@ admin_add_incoming_run (void *cls,
   }
   GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv,
                                       &fts->reserve_pub.eddsa_pub);
+  fts->reserve_history.type = TALER_EXCHANGE_RTT_CREDIT;
+  fts->reserve_history.amount = fts->amount;
+  fts->reserve_history.details.in_details.sender_url
+    = (char *) fts->payto_debit_account; /* remember to NOT free this one... */
   fts->is = is;
   fts->aih
-    = TALER_BANK_admin_add_incoming
-        (TALER_TESTING_interpreter_get_context (is),
+    = TALER_BANK_admin_add_incoming (
+        TALER_TESTING_interpreter_get_context (is),
         &fts->auth,
         &fts->reserve_pub,
         &fts->amount,
@@ -430,6 +451,8 @@ admin_add_incoming_traits (void *cls,
                                            &fts->reserve_priv),
     TALER_TESTING_make_trait_reserve_pub (0,
                                           &fts->reserve_pub),
+    TALER_TESTING_make_trait_reserve_history (0,
+                                              &fts->reserve_history),
     TALER_TESTING_trait_end ()
   };
 
diff --git a/src/testing/testing_api_cmd_exec_closer.c 
b/src/testing/testing_api_cmd_exec_closer.c
index 17eed7a8..78369699 100644
--- a/src/testing/testing_api_cmd_exec_closer.c
+++ b/src/testing/testing_api_cmd_exec_closer.c
@@ -43,6 +43,29 @@ struct CloserState
    * Configuration file used by the closer.
    */
   const char *config_filename;
+
+  /**
+   * Reserve history entry that corresponds to this operation.  Set if @e
+   * expect_close is true.  Will be of type
+   * #TALER_EXCHANGE_RTT_RESERVE_CLOSED.
+   */
+  struct TALER_EXCHANGE_ReserveHistory reserve_history;
+
+  /**
+   * If the closer filled a reserve (@e expect_close is set), this is set to
+   * the reserve's public key.
+   */
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+  /**
+   * Reference to a command to get the @e reserve_pub.
+   */
+  const char *reserve_ref;
+
+  /**
+   * Do we expect the command to actually close a reserve?
+   */
+  int expect_close;
 };
 
 
@@ -61,6 +84,24 @@ closer_run (void *cls,
   struct CloserState *as = cls;
 
   (void) cmd;
+  if (NULL != as->reserve_ref)
+  {
+    const struct TALER_TESTING_Command *rcmd;
+    const struct TALER_ReservePublicKeyP *reserve_pubp;
+
+    rcmd = TALER_TESTING_interpreter_lookup_command (is,
+                                                     as->reserve_ref);
+    if (GNUNET_OK !=
+        TALER_TESTING_get_trait_reserve_pub (rcmd,
+                                             0,
+                                             &reserve_pubp))
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (is);
+      return;
+    }
+    as->reserve_pub = *reserve_pubp;
+  }
   as->closer_proc
     = GNUNET_OS_start_process (GNUNET_NO,
                                GNUNET_OS_INHERIT_STD_ALL,
@@ -127,8 +168,18 @@ closer_traits (void *cls,
     TALER_TESTING_make_trait_process (0, &as->closer_proc),
     TALER_TESTING_trait_end ()
   };
+  struct TALER_TESTING_Trait xtraits[] = {
+    TALER_TESTING_make_trait_process (0, &as->closer_proc),
+    TALER_TESTING_make_trait_reserve_pub (0,
+                                          &as->reserve_pub),
+    TALER_TESTING_make_trait_reserve_history (0,
+                                              &as->reserve_history),
+    TALER_TESTING_trait_end ()
+  };
 
-  return TALER_TESTING_get_trait (traits,
+  return TALER_TESTING_get_trait ((as->expect_close)
+                                  ? xtraits
+                                  : traits,
                                   ret,
                                   trait,
                                   index);
@@ -136,21 +187,65 @@ closer_traits (void *cls,
 
 
 /**
- * Make a "closer" CMD.
+ * Make a "closer" CMD.  Note that it is right now not supported to run the
+ * closer to close multiple reserves in combination with a subsequent reserve
+ * status call, as we cannot generate the traits necessary for multiple closed
+ * reserves.  You can work around this by using multiple closer commands, one
+ * per reserve that is being closed.
  *
  * @param label command label.
  * @param config_filename configuration file for the
  *                        closer to use.
+ * @param expected_amount amount we expect to see wired from a @a 
expected_reserve_ref
+ * @param expected_fee closing fee we expect to see
+ * @param expected_reserve_ref reference to a reserve we expect the closer to 
drain;
+ *          NULL if we do not expect the closer to do anything
  * @return the command.
  */
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_exec_closer (const char *label,
-                               const char *config_filename)
+                               const char *config_filename,
+                               const char *expected_amount,
+                               const char *expected_fee,
+                               const char *expected_reserve_ref)
 {
   struct CloserState *as;
 
   as = GNUNET_new (struct CloserState);
   as->config_filename = config_filename;
+  if (NULL != expected_reserve_ref)
+  {
+    as->expect_close = GNUNET_YES;
+    as->reserve_ref = expected_reserve_ref;
+    if (GNUNET_OK !=
+        TALER_string_to_amount (expected_amount,
+                                &as->reserve_history.amount))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Failed to parse amount `%s' at %s\n",
+                  expected_amount,
+                  label);
+      GNUNET_assert (0);
+    }
+    if (GNUNET_OK !=
+        TALER_string_to_amount (expected_fee,
+                                
&as->reserve_history.details.close_details.fee))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Failed to parse amount `%s' at %s\n",
+                  expected_fee,
+                  label);
+      GNUNET_assert (0);
+    }
+    /* expected amount includes fee, while our argument
+       gives the amount _without_ the fee. So add the fee. */
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_amount_add (&as->reserve_history.amount,
+                                     &as->reserve_history.amount,
+                                     
&as->reserve_history.details.close_details.
+                                     fee));
+    as->reserve_history.type = TALER_EXCHANGE_RTT_CLOSE;
+  }
   {
     struct TALER_TESTING_Command cmd = {
       .cls = as,
diff --git a/src/testing/testing_api_cmd_recoup.c 
b/src/testing/testing_api_cmd_recoup.c
index 13d17835..836ff717 100644
--- a/src/testing/testing_api_cmd_recoup.c
+++ b/src/testing/testing_api_cmd_recoup.c
@@ -18,7 +18,7 @@
 */
 /**
  * @file testing/testing_api_cmd_recoup.c
- * @brief Implement the /revoke and /recoup test commands.
+ * @brief Implement the /recoup test command.
  * @author Marcello Stanisci
  */
 #include "platform.h"
@@ -27,44 +27,6 @@
 #include "taler_testing_lib.h"
 
 
-/**
- * State for a "revoke" CMD.
- */
-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 file name.
-   */
-  const char *config_filename;
-
-  /**
-   * Encoding of the denomination (to revoke) public key hash.
-   */
-  char *dhks;
-
-};
-
-
 /**
  * State for a "pay back" CMD.
  */
@@ -97,6 +59,17 @@ struct RecoupState
    */
   const char *melt_reference;
 
+  /**
+   * If the recoup filled a reserve, this is set to the reserve's public key.
+   */
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+  /**
+   * Reserve history entry, set if this recoup actually filled up a reserve.
+   * Otherwise `reserve_history.type` will be zero.
+   */
+  struct TALER_EXCHANGE_ReserveHistory reserve_history;
+
 };
 
 
@@ -195,8 +168,8 @@ recoup_cb (void *cls,
     return;
   }
 
-  reserve_cmd = TALER_TESTING_interpreter_lookup_command
-                  (is, cref);
+  reserve_cmd = TALER_TESTING_interpreter_lookup_command (is,
+                                                          cref);
   GNUNET_free (cref);
 
   if (NULL == reserve_cmd)
@@ -246,7 +219,6 @@ recoup_cb (void *cls,
     else
     {
       const struct TALER_ReservePrivateKeyP *reserve_priv;
-      struct TALER_ReservePublicKeyP rp;
 
       if (NULL == reserve_pub)
       {
@@ -254,21 +226,28 @@ recoup_cb (void *cls,
         TALER_TESTING_interpreter_fail (is);
         return;
       }
-      if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv
-            (reserve_cmd, idx, &reserve_priv))
+      if (GNUNET_OK !=
+          TALER_TESTING_get_trait_reserve_priv (reserve_cmd,
+                                                idx,
+                                                &reserve_priv))
       {
         GNUNET_break (0);
         TALER_TESTING_interpreter_fail (is);
         return;
       }
       GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
-                                          &rp.eddsa_pub);
-      if (0 != GNUNET_memcmp (reserve_pub, &rp))
+                                          &ps->reserve_pub.eddsa_pub);
+      if (0 != GNUNET_memcmp (reserve_pub,
+                              &ps->reserve_pub))
       {
         GNUNET_break (0);
         TALER_TESTING_interpreter_fail (is);
         return;
       }
+      if (GNUNET_OK ==
+          TALER_amount_is_valid (&ps->reserve_history.amount))
+        ps->reserve_history.type = TALER_EXCHANGE_RTT_RECOUP;
+      /* ps->reserve_history.details.recoup_details.coin_pub; // initialized 
earlier */
     }
     break;
   default:
@@ -313,8 +292,8 @@ recoup_run (void *cls,
     return;
   }
 
-  coin_cmd = TALER_TESTING_interpreter_lookup_command
-               (is, cref);
+  coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
+                                                       cref);
   GNUNET_free (cref);
 
   if (NULL == coin_cmd)
@@ -324,16 +303,20 @@ recoup_run (void *cls,
     return;
   }
 
-  if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
-        (coin_cmd, idx, &coin_priv))
+  if (GNUNET_OK !=
+      TALER_TESTING_get_trait_coin_priv (coin_cmd,
+                                         idx,
+                                         &coin_priv))
   {
     GNUNET_break (0);
     TALER_TESTING_interpreter_fail (is);
     return;
   }
 
-  if (GNUNET_OK != TALER_TESTING_get_trait_blinding_key
-        (coin_cmd, idx, &blinding_key))
+  if (GNUNET_OK !=
+      TALER_TESTING_get_trait_blinding_key (coin_cmd,
+                                            idx,
+                                            &blinding_key))
   {
     GNUNET_break (0);
     TALER_TESTING_interpreter_fail (is);
@@ -341,17 +324,24 @@ recoup_run (void *cls,
   }
   planchet.coin_priv = *coin_priv;
   planchet.blinding_key = *blinding_key;
+  GNUNET_CRYPTO_eddsa_key_get_public (
+    &coin_priv->eddsa_priv,
+    &ps->reserve_history.details.recoup_details.coin_pub.eddsa_pub);
 
-  if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub
-        (coin_cmd, idx, &denom_pub))
+  if (GNUNET_OK !=
+      TALER_TESTING_get_trait_denom_pub (coin_cmd,
+                                         idx,
+                                         &denom_pub))
   {
     GNUNET_break (0);
     TALER_TESTING_interpreter_fail (is);
     return;
   }
 
-  if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig
-        (coin_cmd, idx, &coin_sig))
+  if (GNUNET_OK !=
+      TALER_TESTING_get_trait_denom_sig (coin_cmd,
+                                         idx,
+                                         &coin_sig))
   {
     GNUNET_break (0);
     TALER_TESTING_interpreter_fail (is);
@@ -359,7 +349,7 @@ recoup_run (void *cls,
   }
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Trying to get '%s..' paid back\n",
+              "Trying to recoup denomination '%s'\n",
               TALER_B2S (&denom_pub->h_key));
 
   ps->ph = TALER_EXCHANGE_recoup (is->exchange,
@@ -373,31 +363,6 @@ recoup_run (void *cls,
 }
 
 
-/**
- * Cleanup the state.
- *
- * @param cls closure, must be a `struct RevokeState`.
- * @param cmd the command which is being cleaned up.
- */
-static 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_non_null (rs->dhks);
-  GNUNET_free (rs);
-}
-
-
 /**
  * Cleanup the "recoup" CMD state, and possibly cancel
  * a pending operation thereof.
@@ -420,7 +385,8 @@ recoup_cleanup (void *cls,
 
 
 /**
- * Offer internal data from a "revoke" CMD to other CMDs.
+ * Offer internal data from a "recoup" CMD state to other
+ * commands.
  *
  * @param cls closure
  * @param[out] ret result (could be anything)
@@ -429,87 +395,29 @@ recoup_cleanup (void *cls,
  * @return #GNUNET_OK on success
  */
 static int
-revoke_traits (void *cls,
+recoup_traits (void *cls,
                const 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 "revoke" command.  The core of the function
- * is to call the "keyup" utility passing it the base32
- * encoding of the denomination to revoke.
- *
- * @param cls closure.
- * @param cmd the command to execute.
- * @param is the interpreter state.
- */
-static 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;
-  const 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);
+  struct RecoupState *ps = cls;
 
-  if (NULL == coin_cmd)
+  if (ps->reserve_history.type != TALER_EXCHANGE_RTT_RECOUP)
+    return GNUNET_SYSERR; /* no traits */
   {
-    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);
+    struct TALER_TESTING_Trait traits[] = {
+      TALER_TESTING_make_trait_reserve_pub (0,
+                                            &ps->reserve_pub),
+      TALER_TESTING_make_trait_reserve_history (0,
+                                                &ps->reserve_history),
+      TALER_TESTING_trait_end ()
+    };
 
-  if (NULL == rs->revoke_proc)
-  {
-    GNUNET_break (0);
-    TALER_TESTING_interpreter_fail (is);
-    return;
+    return TALER_TESTING_get_trait (traits,
+                                    ret,
+                                    trait,
+                                    index);
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Revoke is ongoing..\n");
-
-  is->reload_keys = GNUNET_OK;
-  TALER_TESTING_wait_for_sigchld (is);
 }
 
 
@@ -521,13 +429,15 @@ revoke_run (void *cls,
  * @param coin_reference reference to any command which
  *        offers a coin & reserve private key.
  * @param melt_reference NULL if coin was not refreshed
+ * @param amount how much do we expect to recoup?
  * @return the command.
  */
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_recoup (const char *label,
                           unsigned int expected_response_code,
                           const char *coin_reference,
-                          const char *melt_reference)
+                          const char *melt_reference,
+                          const char *amount)
 {
   struct RecoupState *ps;
 
@@ -535,49 +445,24 @@ TALER_TESTING_cmd_recoup (const char *label,
   ps->expected_response_code = expected_response_code;
   ps->coin_reference = coin_reference;
   ps->melt_reference = melt_reference;
+  if ( (NULL != amount) &&
+       (GNUNET_OK !=
+        TALER_string_to_amount (amount,
+                                &ps->reserve_history.amount)) )
   {
-    struct TALER_TESTING_Command cmd = {
-      .cls = ps,
-      .label = label,
-      .run = &recoup_run,
-      .cleanup = &recoup_cleanup
-    };
-
-    return cmd;
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to parse amount `%s' at %s\n",
+                amount,
+                label);
+    GNUNET_assert (0);
   }
-}
-
-
-/**
- * Make a "revoke" command.
- *
- * @param label the command label.
- * @param expected_response_code expected HTTP status code.
- * @param coin_reference reference to a CMD that will offer the
- *        denomination to revoke.
- * @param config_filename configuration file name.
- * @return the 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;
-
-  rs = GNUNET_new (struct RevokeState);
-  rs->expected_response_code = expected_response_code;
-  rs->coin_reference = coin_reference;
-  rs->config_filename = config_filename;
   {
     struct TALER_TESTING_Command cmd = {
-      .cls = rs,
+      .cls = ps,
       .label = label,
-      .run = &revoke_run,
-      .cleanup = &revoke_cleanup,
-      .traits = &revoke_traits
+      .run = &recoup_run,
+      .cleanup = &recoup_cleanup,
+      .traits = &recoup_traits
     };
 
     return cmd;
diff --git a/src/testing/testing_api_cmd_revoke.c 
b/src/testing/testing_api_cmd_revoke.c
new file mode 100644
index 00000000..1006fbc3
--- /dev/null
+++ b/src/testing/testing_api_cmd_revoke.c
@@ -0,0 +1,224 @@
+/*
+  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 testing/testing_api_cmd_revoke.c
+ * @brief Implement the revoke test command.
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_testing_lib.h"
+
+
+/**
+ * State for a "revoke" CMD.
+ */
+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 file name.
+   */
+  const char *config_filename;
+
+  /**
+   * Encoding of the denomination (to revoke) public key hash.
+   */
+  char *dhks;
+
+};
+
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, must be a `struct RevokeState`.
+ * @param cmd the command which is being cleaned up.
+ */
+static 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_non_null (rs->dhks);
+  GNUNET_free (rs);
+}
+
+
+/**
+ * Offer internal data from a "revoke" CMD to other CMDs.
+ *
+ * @param cls closure
+ * @param[out] ret result (could be anything)
+ * @param trait name of the trait
+ * @param index index number of the object to offer.
+ * @return #GNUNET_OK on success
+ */
+static int
+revoke_traits (void *cls,
+               const 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 "revoke" command.  The core of the function
+ * is to call the "keyup" utility passing it the base32
+ * encoding of the denomination to revoke.
+ *
+ * @param cls closure.
+ * @param cmd the command to execute.
+ * @param is the interpreter state.
+ */
+static 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;
+  const 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 "revoke" command.
+ *
+ * @param label the command label.
+ * @param expected_response_code expected HTTP status code.
+ * @param coin_reference reference to a CMD that will offer the
+ *        denomination to revoke.
+ * @param config_filename configuration file name.
+ * @return the 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;
+
+  rs = GNUNET_new (struct RevokeState);
+  rs->expected_response_code = expected_response_code;
+  rs->coin_reference = coin_reference;
+  rs->config_filename = config_filename;
+  {
+    struct TALER_TESTING_Command cmd = {
+      .cls = rs,
+      .label = label,
+      .run = &revoke_run,
+      .cleanup = &revoke_cleanup,
+      .traits = &revoke_traits
+    };
+
+    return cmd;
+  }
+}
diff --git a/src/testing/testing_api_cmd_status.c 
b/src/testing/testing_api_cmd_status.c
index bb132645..71714d3f 100644
--- a/src/testing/testing_api_cmd_status.c
+++ b/src/testing/testing_api_cmd_status.c
@@ -48,6 +48,11 @@ struct StatusState
    */
   const char *expected_balance;
 
+  /**
+   * Public key of the reserve being analyzed.
+   */
+  const struct TALER_ReservePublicKeyP *reserve_pubp;
+
   /**
    * Expected HTTP response code.
    */
@@ -60,6 +65,175 @@ struct StatusState
 };
 
 
+/**
+ * Compare @a h1 and @a h2.
+ *
+ * @param h1 a history entry
+ * @param h2 a history entry
+ * @return 0 if @a h1 and @a h2 are equal
+ */
+static int
+history_entry_cmp (const struct TALER_EXCHANGE_ReserveHistory *h1,
+                   const struct TALER_EXCHANGE_ReserveHistory *h2)
+{
+  if (h1->type != h2->type)
+    return 1;
+  switch (h1->type)
+  {
+  case TALER_EXCHANGE_RTT_CREDIT:
+    if ( (0 ==
+          TALER_amount_cmp (&h1->amount,
+                            &h2->amount)) &&
+         (h1->details.in_details.wire_reference_size ==
+          h2->details.in_details.wire_reference_size) &&
+         (0 == strcasecmp (h1->details.in_details.sender_url,
+                           h2->details.in_details.sender_url)) &&
+         (0 == memcmp (h1->details.in_details.wire_reference,
+                       h2->details.in_details.wire_reference,
+                       h1->details.in_details.wire_reference_size)) &&
+         (h1->details.in_details.timestamp.abs_value_us ==
+          h2->details.in_details.timestamp.abs_value_us) )
+      return 0;
+    return 1;
+  case TALER_EXCHANGE_RTT_WITHDRAWAL:
+    if ( (0 ==
+          TALER_amount_cmp (&h1->amount,
+                            &h2->amount)) &&
+         (0 ==
+          TALER_amount_cmp (&h1->details.withdraw.fee,
+                            &h2->details.withdraw.fee)) )
+      /* testing_api_cmd_withdraw doesn't set the out_authorization_sig,
+         so we cannot test for it here. but if the amount matches,
+         that should be good enough. */
+      return 0;
+    return 1;
+  case TALER_EXCHANGE_RTT_RECOUP:
+    /* exchange_sig, exchange_pub and timestamp are NOT available
+       from the original recoup response, hence here NOT check(able/ed) */
+    if ( (0 ==
+          TALER_amount_cmp (&h1->amount,
+                            &h2->amount)) &&
+         (0 ==
+          GNUNET_memcmp (&h1->details.recoup_details.coin_pub,
+                         &h2->details.recoup_details.coin_pub)) )
+      return 0;
+    return 1;
+  case TALER_EXCHANGE_RTT_CLOSE:
+    /* testing_api_cmd_exec_closer doesn't set the
+       receiver_account_details, exchange_sig, exchange_pub or wtid or 
timestamp
+       so we cannot test for it here. but if the amount matches,
+       that should be good enough. */
+    if ( (0 ==
+          TALER_amount_cmp (&h1->amount,
+                            &h2->amount)) &&
+         (0 ==
+          TALER_amount_cmp (&h1->details.close_details.fee,
+                            &h2->details.close_details.fee)) )
+      return 0;
+    return 1;
+  }
+  GNUNET_assert (0);
+  return 1;
+}
+
+
+/**
+ * Check if @a cmd changed the reserve, if so, find the
+ * entry in @a history and set the respective index in @a found
+ * to #GNUNET_YES. If the entry is not found, return #GNUNET_SYSERR.
+ *
+ * @param reserve_pub public key of the reserve for which we have the @a 
history
+ * @param cmd command to analyze for impact on history
+ * @param history_length number of entries in @a history and @a found
+ * @param history history to check
+ * @param[in,out] found array to update
+ * @return #GNUNET_OK if @a cmd action on reserve was found in @a history
+ */
+static int
+analyze_command (const struct TALER_ReservePublicKeyP *reserve_pub,
+                 const struct TALER_TESTING_Command *cmd,
+                 unsigned int history_length,
+                 const struct TALER_EXCHANGE_ReserveHistory *history,
+                 int *found)
+{
+  if (TALER_TESTING_cmd_is_batch (cmd))
+  {
+#define BATCH_INDEX 1
+    struct TALER_TESTING_Command *cur;
+    struct TALER_TESTING_Command *bcmd;
+
+    cur = TALER_TESTING_cmd_batch_get_current (cmd);
+    if (GNUNET_OK !=
+        TALER_TESTING_get_trait_cmd (cmd,
+                                     BATCH_INDEX,
+                                     &bcmd))
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    for (unsigned int i = 0; NULL != bcmd[i].label; i++)
+    {
+      struct TALER_TESTING_Command *step = &bcmd[i];
+
+      if (step == cur)
+        break; /* if *we* are in a batch, make sure not to analyze commands 
past 'now' */
+      if (GNUNET_OK !=
+          analyze_command (reserve_pub,
+                           step,
+                           history_length,
+                           history,
+                           found))
+        return GNUNET_SYSERR;
+    }
+    return GNUNET_OK;
+  }
+  else
+  {
+    const struct TALER_ReservePublicKeyP *rp;
+    const struct TALER_EXCHANGE_ReserveHistory *he;
+
+    if (GNUNET_OK !=
+        TALER_TESTING_get_trait_reserve_pub (cmd,
+                                             0,
+                                             &rp))
+      return GNUNET_OK; /* command does nothing for reserves */
+    if (0 !=
+        GNUNET_memcmp (rp,
+                       reserve_pub))
+      return GNUNET_OK; /* command affects some _other_ reserve */
+    if (GNUNET_OK !=
+        TALER_TESTING_get_trait_reserve_history (cmd,
+                                                 0,
+                                                 &he))
+    {
+      /* NOTE: good for debugging for now, might later reduce debug
+         level in case there are commands that legitimately don't
+         impact the reserve history but have the public key trait */
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Command `%s' has the reserve_pub trait, but does not 
reserve history trait\n",
+                  cmd->label);
+      return GNUNET_OK; /* command does nothing for reserves */
+    }
+    for (unsigned int i = 0; i<history_length; i++)
+    {
+      if (found[i])
+        continue; /* already found, skip */
+      if (0 ==
+          history_entry_cmp (he,
+                             &history[i]))
+      {
+        found[i] = GNUNET_YES;
+        return GNUNET_OK;
+      }
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Command `%s' reserve history entry not found\n",
+                cmd->label);
+    return GNUNET_SYSERR;
+  }
+}
+
+
 /**
  * Check that the reserve balance and HTTP response code are
  * both acceptable.
@@ -83,6 +257,7 @@ reserve_status_cb (void *cls,
                    const struct TALER_EXCHANGE_ReserveHistory *history)
 {
   struct StatusState *ss = cls;
+  struct TALER_TESTING_Interpreter *is = ss->is;
   struct TALER_Amount eb;
 
   ss->rsh = NULL;
@@ -111,24 +286,40 @@ reserve_status_cb (void *cls,
     TALER_TESTING_interpreter_fail (ss->is);
     return;
   }
+  {
+    int found[history_length];
 
-  /**
-   * TODO (#6049): We should check that 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).
-   *
-   * IDEA: Maybe realize this via another trait, some kind of
-   * "reserve history update trait" which returns information about
-   * how the command changes the history (provided only by commands
-   * that change reserve balances)?
-   *///
-  TALER_TESTING_interpreter_next (ss->is);
+    memset (found, 0, sizeof (found));
+    for (unsigned int i = 0; i<=is->ip; i++)
+    {
+      struct TALER_TESTING_Command *cmd = &is->commands[i];
+
+      if (GNUNET_OK !=
+          analyze_command (ss->reserve_pubp,
+                           cmd,
+                           history_length,
+                           history,
+                           found))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Entry for command `%s' missing in history\n",
+                    cmd->label);
+        TALER_TESTING_interpreter_fail (ss->is);
+        return;
+      }
+    }
+    for (unsigned int i = 0; i<history_length; i++)
+      if (! found[i])
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "History entry at index %u of type %d not justified by 
command history\n",
+                    i,
+                    history[i].type);
+        TALER_TESTING_interpreter_fail (ss->is);
+        return;
+      }
+  }
+  TALER_TESTING_interpreter_next (is);
 }
 
 
@@ -146,7 +337,6 @@ status_run (void *cls,
 {
   struct StatusState *ss = cls;
   const struct TALER_TESTING_Command *create_reserve;
-  const struct TALER_ReservePublicKeyP *reserve_pubp;
 
   ss->is = is;
   create_reserve
@@ -162,7 +352,7 @@ status_run (void *cls,
   if (GNUNET_OK !=
       TALER_TESTING_get_trait_reserve_pub (create_reserve,
                                            0,
-                                           &reserve_pubp))
+                                           &ss->reserve_pubp))
   {
     GNUNET_break (0);
     TALER_LOG_ERROR ("Failed to find reserve_pub for status query\n");
@@ -170,7 +360,7 @@ status_run (void *cls,
     return;
   }
   ss->rsh = TALER_EXCHANGE_reserves_get (is->exchange,
-                                         reserve_pubp,
+                                         ss->reserve_pubp,
                                          &reserve_status_cb,
                                          ss);
 }
diff --git a/src/testing/testing_api_cmd_withdraw.c 
b/src/testing/testing_api_cmd_withdraw.c
index 5bb34a04..995eca67 100644
--- a/src/testing/testing_api_cmd_withdraw.c
+++ b/src/testing/testing_api_cmd_withdraw.c
@@ -94,6 +94,12 @@ struct WithdrawState
    */
   struct TALER_PlanchetSecretsP ps;
 
+  /**
+   * Reserve history entry that corresponds to this operation.
+   * Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL.
+   */
+  struct TALER_EXCHANGE_ReserveHistory reserve_history;
+
   /**
    * Withdraw handle (while operation is running).
    */
@@ -286,8 +292,10 @@ withdraw_run (void *cls,
   const struct TALER_EXCHANGE_DenomPublicKey *dpk;
 
   (void) cmd;
-  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);
@@ -322,6 +330,16 @@ withdraw_run (void *cls,
      * would free the old one. */
     ws->pk = TALER_EXCHANGE_copy_denomination_key (dpk);
   }
+  else
+  {
+    ws->amount = ws->pk->value;
+  }
+  ws->reserve_history.type = TALER_EXCHANGE_RTT_WITHDRAWAL;
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_add (&ws->reserve_history.amount,
+                                   &ws->amount,
+                                   &ws->pk->fee_withdraw));
+  ws->reserve_history.details.withdraw.fee = ws->pk->fee_withdraw;
   ws->wsh = TALER_EXCHANGE_withdraw (is->exchange,
                                      ws->pk,
                                      rp,
@@ -401,8 +419,8 @@ withdraw_traits (void *cls,
 
   /* We offer the reserve key where these coins were withdrawn
    * from. */
-  reserve_cmd = TALER_TESTING_interpreter_lookup_command
-                  (ws->is, ws->reserve_reference);
+  reserve_cmd = TALER_TESTING_interpreter_lookup_command (ws->is,
+                                                          
ws->reserve_reference);
 
   if (NULL == reserve_cmd)
   {
@@ -434,6 +452,9 @@ withdraw_traits (void *cls,
       = GNUNET_strdup (TALER_EXCHANGE_get_base_url (ws->is->exchange));
   {
     struct TALER_TESTING_Trait traits[] = {
+      /* history entry MUST be first due to response code logic below! */
+      TALER_TESTING_make_trait_reserve_history (0,
+                                                &ws->reserve_history),
       TALER_TESTING_make_trait_coin_priv (0 /* only one coin */,
                                           &ws->ps.coin_priv),
       TALER_TESTING_make_trait_blinding_key (0 /* only one coin */,
@@ -453,7 +474,9 @@ withdraw_traits (void *cls,
       TALER_TESTING_trait_end ()
     };
 
-    return TALER_TESTING_get_trait (traits,
+    return TALER_TESTING_get_trait ((ws->expected_response_code == MHD_HTTP_OK)
+                                    ? &traits[0] /* we have reserve history */
+                                    : &traits[1],/* skip reserve history */
                                     ret,
                                     trait,
                                     index);
diff --git a/src/testing/testing_api_helpers_exchange.c 
b/src/testing/testing_api_helpers_exchange.c
index 8da7a618..d7195a9c 100644
--- a/src/testing/testing_api_helpers_exchange.c
+++ b/src/testing/testing_api_helpers_exchange.c
@@ -406,6 +406,13 @@ sign_keys_for_exchange (void *cls,
     si->ec->exchange_url = NULL;
     return GNUNET_NO;
   }
+  if (GNUNET_OK !=
+      TALER_TESTING_url_port_free (si->ec->exchange_url))
+  {
+    GNUNET_free (si->ec->exchange_url);
+    si->ec->exchange_url = NULL;
+    return GNUNET_NO;
+  }
 
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_string (cfg,
@@ -421,7 +428,12 @@ sign_keys_for_exchange (void *cls,
     si->ec->auditor_url = NULL;
     return GNUNET_SYSERR;
   }
-
+  if (GNUNET_OK !=
+      TALER_TESTING_url_port_free (si->ec->auditor_url))
+  {
+    ret = GNUNET_NO;
+    goto fail;
+  }
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_filename (cfg,
                                                "paths",
diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c
index 7c3a7aaa..5bfb7fe0 100644
--- a/src/testing/testing_api_loop.c
+++ b/src/testing/testing_api_loop.c
@@ -68,20 +68,29 @@ TALER_TESTING_interpreter_lookup_command (struct 
TALER_TESTING_Interpreter *is,
     {
 #define BATCH_INDEX 1
       struct TALER_TESTING_Command *batch;
+      struct TALER_TESTING_Command *current;
+      const struct TALER_TESTING_Command *match;
 
+      current = TALER_TESTING_cmd_batch_get_current (cmd);
       GNUNET_assert (GNUNET_OK ==
                      TALER_TESTING_get_trait_cmd (cmd,
                                                   BATCH_INDEX,
                                                   &batch));
+      /* We must do the loop forward, but we can find the last match */
+      match = NULL;
       for (unsigned int j = 0;
            NULL != (cmd = &batch[j])->label;
            j++)
       {
+        if (current == cmd)
+          break; /* do not go past current command */
         if ( (NULL != cmd->label) &&
              (0 == strcmp (cmd->label,
                            label)) )
-          return cmd;
+          match = cmd;
       }
+      if (NULL != match)
+        return match;
     }
   }
   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
diff --git a/src/testing/testing_api_trait_reserve_pub.c 
b/src/testing/testing_api_trait_reserve_history.c
similarity index 53%
copy from src/testing/testing_api_trait_reserve_pub.c
copy to src/testing/testing_api_trait_reserve_history.c
index a158114b..b458dbd6 100644
--- a/src/testing/testing_api_trait_reserve_pub.c
+++ b/src/testing/testing_api_trait_reserve_history.c
@@ -17,10 +17,9 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file testing/testing_api_trait_reserve_pub.c
- * @brief implements reserve public key trait
+ * @file testing/testing_api_trait_reserve_history.c
+ * @brief implements reserve hostry trait
  * @author Christian Grothoff
- * @author Marcello Stanisci
  */
 #include "platform.h"
 #include "taler_json_lib.h"
@@ -28,49 +27,50 @@
 #include "taler_signatures.h"
 #include "taler_testing_lib.h"
 
-#define TALER_TESTING_TRAIT_RESERVE_PUBLIC_KEY \
-  "reserve-public-key"
+#define TALER_TESTING_TRAIT_RESERVE_HISTORY \
+  "reserve-history-entry"
+
 
 /**
- * Obtain a reserve public key from a @a cmd.
+ * Obtain a reserve history entry from a @a cmd.
  *
- * @param cmd command to extract the reserve pub from.
- * @param index reserve pub's index number.
- * @param[out] reserve_pub set to the reserve pub.
+ * @param cmd command to extract the reserve history from.
+ * @param index reserve history's index number.
+ * @param[out] rhp set to the reserve history.
  * @return #GNUNET_OK on success.
  */
 int
-TALER_TESTING_get_trait_reserve_pub
-  (const struct TALER_TESTING_Command *cmd,
+TALER_TESTING_get_trait_reserve_history (
+  const struct TALER_TESTING_Command *cmd,
   unsigned int index,
-  const struct TALER_ReservePublicKeyP **reserve_pub)
+  const struct TALER_EXCHANGE_ReserveHistory **rhp)
 {
   return cmd->traits (cmd->cls,
-                      (const void **) reserve_pub,
-                      TALER_TESTING_TRAIT_RESERVE_PUBLIC_KEY,
+                      (const void **) rhp,
+                      TALER_TESTING_TRAIT_RESERVE_HISTORY,
                       index);
 }
 
 
 /**
- * Offer a reserve public key.
+ * Offer a reserve history entry.
  *
- * @param index reserve pub's index number.
- * @param reserve_pub reserve public key to offer.
+ * @param index reserve pubs's index number.
+ * @param rh reserve history entry to offer.
  * @return the trait.
  */
 struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_reserve_pub
-  (unsigned int index,
-  const struct TALER_ReservePublicKeyP *reserve_pub)
+TALER_TESTING_make_trait_reserve_history (
+  unsigned int index,
+  const struct TALER_EXCHANGE_ReserveHistory *rh)
 {
   struct TALER_TESTING_Trait ret = {
     .index = index,
-    .trait_name = TALER_TESTING_TRAIT_RESERVE_PUBLIC_KEY,
-    .ptr = (const void *) reserve_pub
+    .trait_name = TALER_TESTING_TRAIT_RESERVE_HISTORY,
+    .ptr = (const void *) rh
   };
   return ret;
 }
 
 
-/* end of testing_api_trait_reserve_pub.c */
+/* end of testing_api_trait_reserve_history.c */
diff --git a/src/testing/testing_api_trait_reserve_pub.c 
b/src/testing/testing_api_trait_reserve_pub.c
index a158114b..743a10e9 100644
--- a/src/testing/testing_api_trait_reserve_pub.c
+++ b/src/testing/testing_api_trait_reserve_pub.c
@@ -45,6 +45,8 @@ TALER_TESTING_get_trait_reserve_pub
   unsigned int index,
   const struct TALER_ReservePublicKeyP **reserve_pub)
 {
+  if (NULL == cmd->traits)
+    return GNUNET_SYSERR;
   return cmd->traits (cmd->cls,
                       (const void **) reserve_pub,
                       TALER_TESTING_TRAIT_RESERVE_PUBLIC_KEY,

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



reply via email to

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