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: finished implementi


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] branch master updated: finished implementing #4956 in principle, but not yet tested
Date: Thu, 20 Apr 2017 21:38:10 +0200

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

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new 27c921c  finished implementing #4956 in principle, but not yet tested
27c921c is described below

commit 27c921c7c45f8ea8fed5c945a9e0ae0cfcc1c8e9
Author: Christian Grothoff <address@hidden>
AuthorDate: Thu Apr 20 21:38:02 2017 +0200

    finished implementing #4956 in principle, but not yet tested
---
 src/auditor/taler-auditor.c                        |  23 +-
 src/exchange-lib/exchange_api_reserve.c            |   7 +-
 src/exchange/taler-exchange-aggregator.c           | 368 ++++++++++++++++++++-
 src/exchange/taler-exchange-httpd_responses.c      |   7 +-
 .../test-taler-exchange-aggregator-postgres.conf   |   9 +
 src/exchange/test_taler_exchange_httpd.conf        |  10 +
 src/exchangedb/exchangedb.conf                     |   9 +
 src/exchangedb/plugin_exchangedb_common.c          |   2 -
 src/exchangedb/plugin_exchangedb_postgres.c        | 215 ++++++++++--
 src/exchangedb/test-exchange-db-postgres.conf      |  11 +
 src/exchangedb/test_exchangedb.c                   |  34 +-
 src/include/taler_exchange_service.h               |   2 +-
 src/include/taler_exchangedb_lib.h                 |   1 +
 src/include/taler_exchangedb_plugin.h              |  49 ++-
 src/include/taler_signatures.h                     |   9 +-
 15 files changed, 691 insertions(+), 65 deletions(-)

diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index 5db8489..fa8940f 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -100,6 +100,11 @@ static struct TALER_AUDITORDB_Plugin *adb;
 static struct TALER_AUDITORDB_Session *asession;
 
 /**
+ * After how long should idle reserves be closed?
+ */
+static struct GNUNET_TIME_Relative idle_reserve_expiration_time;
+
+/**
  * Master public key of the exchange to audit.
  */
 static struct TALER_MasterPublicKeyP master_pub;
@@ -672,7 +677,7 @@ handle_reserve_in (void *cls,
               TALER_B2S (reserve_pub),
               TALER_amount2s (credit));
   expiry = GNUNET_TIME_absolute_add (execution_date,
-                                     TALER_IDLE_RESERVE_EXPIRATION_TIME);
+                                     idle_reserve_expiration_time);
   rs->a_expiration_date = GNUNET_TIME_absolute_max (rs->a_expiration_date,
                                                     expiry);
   return GNUNET_OK;
@@ -973,7 +978,7 @@ handle_payback_by_reserve (void *cls,
               TALER_B2S (reserve_pub),
               TALER_amount2s (amount));
   expiry = GNUNET_TIME_absolute_add (timestamp,
-                                     TALER_IDLE_RESERVE_EXPIRATION_TIME);
+                                     idle_reserve_expiration_time);
   rs->a_expiration_date = GNUNET_TIME_absolute_max (rs->a_expiration_date,
                                                     expiry);
   return GNUNET_OK;
@@ -1002,7 +1007,7 @@ handle_reserve_closed (void *cls,
                       const struct TALER_Amount *closing_fee,
                       const struct TALER_ReservePublicKeyP *reserve_pub,
                       const json_t *receiver_account,
-                      const json_t *transfer_details)
+                      const struct TALER_WireTransferIdentifierRawP 
*transfer_details)
 {
   struct ReserveContext *rc = cls;
   struct GNUNET_HashCode key;
@@ -3613,6 +3618,18 @@ run (void *cls,
     global_ret = 1;
     return;
   }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_time (cfg,
+                                          "exchangedb",
+                                          "IDLE_RESERVE_EXPIRATION_TIME",
+                                          &idle_reserve_expiration_time))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "exchangedb",
+                               "IDLE_RESERVE_EXPIRATION_TIME");
+    global_ret = 1;
+    return;
+  }
   if (NULL ==
       (edb = TALER_EXCHANGEDB_plugin_load (cfg)))
   {
diff --git a/src/exchange-lib/exchange_api_reserve.c 
b/src/exchange-lib/exchange_api_reserve.c
index de243b0..c7f59c2 100644
--- a/src/exchange-lib/exchange_api_reserve.c
+++ b/src/exchange-lib/exchange_api_reserve.c
@@ -335,8 +335,8 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle 
*exchange,
       struct GNUNET_JSON_Specification closing_spec[] = {
         GNUNET_JSON_spec_json ("receiver_account_details",
                               
&rhistory[off].details.close_details.receiver_account_details),
-        GNUNET_JSON_spec_json ("wire_transfer",
-                              
&rhistory[off].details.close_details.transfer_details),
+        GNUNET_JSON_spec_fixed_auto ("wire_transfer",
+                                    &rhistory[off].details.close_details.wtid),
         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
                                      
&rhistory[off].details.close_details.exchange_sig),
         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
@@ -362,8 +362,7 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle 
*exchange,
                         &amount);
       TALER_JSON_hash 
(rhistory[off].details.close_details.receiver_account_details,
                       &rcc.h_wire);
-      TALER_JSON_hash 
(rhistory[off].details.close_details.receiver_account_details,
-                      &rcc.h_transfer);
+      rcc.wtid = rhistory[off].details.close_details.wtid;
       rcc.purpose.size = htonl (sizeof (rcc));
       rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
       rcc.reserve_pub = *reserve_pub;
diff --git a/src/exchange/taler-exchange-aggregator.c 
b/src/exchange/taler-exchange-aggregator.c
index e958793..54757d8 100644
--- a/src/exchange/taler-exchange-aggregator.c
+++ b/src/exchange/taler-exchange-aggregator.c
@@ -48,7 +48,7 @@ struct WirePlugin
    * Handle to the plugin.
    */
   struct TALER_WIRE_Plugin *wire_plugin;
-
+  
   /**
    * Name of the plugin.
    */
@@ -178,6 +178,34 @@ struct AggregationUnit
 
 
 /**
+ * Context we use while closing a reserve.
+ */
+struct CloseTransferContext
+{
+  /**
+   * Handle for preparing the wire transfer.
+   */
+  struct TALER_WIRE_PrepareHandle *ph;
+
+  /**
+   * Our database session.
+   */
+  struct TALER_EXCHANGEDB_Session *session;
+
+  /**
+   * Wire transfer method.
+   */
+  char *type;
+};
+
+
+/**
+ * Active context while processing reserve closing,
+ * or NULL.
+ */
+static struct CloseTransferContext *ctc;
+
+/**
  * Which currency is used by this exchange?
  */
 static char *exchange_currency_string;
@@ -236,6 +264,11 @@ static int global_ret;
 static int test_mode;
 
 /**
+ * Did #run_reserve_closures() have any work during its last run?
+ */
+static int reserves_idle;
+
+/**
  * Limit on the number of transactions we aggregate at once.  Note
  * that the limit must be big enough to ensure that when transactions
  * of the smallest possible unit are aggregated, they do surpass the
@@ -666,7 +699,8 @@ aggregate_cb (void *cls,
 
 
 /**
- * Function to be called with the prepared transfer data.
+ * Function to be called with the prepared transfer data
+ * when running an aggregation on a merchant.
  *
  * @param cls closure with the `struct AggregationUnit`
  * @param buf transaction data to persist, NULL on error
@@ -679,6 +713,319 @@ prepare_cb (void *cls,
 
 
 /**
+ * Main work function that finds and triggers transfers for reserves
+ * closures.
+ *
+ * @param cls closure
+ */
+static void
+run_reserve_closures (void *cls);
+
+
+/**
+ * Main work function that queries the DB and aggregates transactions
+ * into larger wire transfers.
+ *
+ * @param cls NULL
+ */
+static void
+run_aggregation (void *cls);
+
+
+/**
+ * Function to be called with the prepared transfer data
+ * when closing a reserve.
+ *
+ * @param cls closure with a `struct CloseTransferContext`
+ * @param buf transaction data to persist, NULL on error
+ * @param buf_size number of bytes in @a buf, 0 on error
+ */
+static void
+prepare_close_cb (void *cls,
+                 const char *buf,
+                 size_t buf_size)
+{
+  GNUNET_assert (cls == ctc);  
+  ctc->ph = NULL;
+  if (NULL == buf)
+  {
+    GNUNET_break (0); /* why? how to best recover? */
+    db_plugin->rollback (db_plugin->cls,
+                         ctc->session);
+    /* start again */
+    GNUNET_free (ctc->type);
+    GNUNET_free (ctc);
+    ctc = NULL;
+    task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+                                     NULL);
+    return;
+  }
+
+  /* Commit our intention to execute the wire transfer! */
+  if (GNUNET_OK !=
+      db_plugin->wire_prepare_data_insert (db_plugin->cls,
+                                           ctc->session,
+                                           ctc->type,
+                                           buf,
+                                           buf_size))
+  {
+    GNUNET_break (0); /* why? how to best recover? */
+    db_plugin->rollback (db_plugin->cls,
+                         ctc->session);
+    /* start again */
+    task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+                                     NULL);
+    GNUNET_free (ctc->type);
+    GNUNET_free (ctc);
+    ctc = NULL;
+    return;
+  }
+
+  /* finally commit */
+  if (GNUNET_OK !=
+      db_plugin->commit (db_plugin->cls,
+                        ctc->session))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               "Failed to commit database transaction!\n");
+  }
+  GNUNET_free (ctc->type);
+  GNUNET_free (ctc);
+  ctc = NULL;
+  task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+                                  NULL);
+}
+
+
+/**
+ * Function called with details about expired reserves.
+ * We trigger the reserve closure by inserting the respective
+ * closing record and prewire instructions into the respective
+ * tables. 
+ *
+ * @param cls a `struct TALER_EXCHANGEDB_Session *`
+ * @param reserve_pub public key of the reserve
+ * @param left amount left in the reserve
+ * @param account_details information about the reserve's bank account
+ * @param expiration_date when did the reserve expire
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ */
+static int
+expired_reserve_cb (void *cls,
+                   const struct TALER_ReservePublicKeyP *reserve_pub,
+                   const struct TALER_Amount *left,
+                   const json_t *account_details,
+                   struct GNUNET_TIME_Absolute expiration_date)
+{
+  struct TALER_EXCHANGEDB_Session *session = cls;
+  struct GNUNET_TIME_Absolute now;
+  struct TALER_WireTransferIdentifierRawP wtid;
+  struct TALER_Amount amount_without_fee;
+  const struct TALER_Amount *closing_fee;
+  int ret;
+  int iret;
+  const char *type;
+  struct WirePlugin *wp;
+
+  GNUNET_assert (NULL == ctc);
+  now = GNUNET_TIME_absolute_get ();
+
+  /* lookup wire plugin */
+  type = extract_type (account_details);
+  if (NULL == type)
+  {
+    GNUNET_break (0);
+    db_plugin->rollback (db_plugin->cls,
+                        session);
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    return GNUNET_SYSERR;
+  }
+  wp = find_plugin (type);
+  if (NULL == wp)
+  {
+    GNUNET_break (0);
+    db_plugin->rollback (db_plugin->cls,
+                        session);
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    return GNUNET_SYSERR;
+  }
+  
+  /* lookup `closing_fee` */
+  if (GNUNET_OK !=
+      update_fees (wp,
+                  now,
+                  session))
+  {
+    GNUNET_break (0);
+    db_plugin->rollback (db_plugin->cls,
+                        session);
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    return GNUNET_SYSERR;
+  }
+  closing_fee = &wp->af->closing_fee;
+
+  /* calculate transfer amount */
+  ret = TALER_amount_subtract (&amount_without_fee,
+                              left,
+                              closing_fee);
+  if ( (GNUNET_SYSERR == ret) ||
+       (GNUNET_NO == ret) )
+  {
+    /* Closing fee higher than remaining balance, close
+       without wire transfer. */
+    closing_fee = left;
+    TALER_amount_get_zero (left->currency,
+                          &amount_without_fee);
+  }
+
+  /* NOTE: sizeof (*reserve_pub) == sizeof (wtid) right now, but to
+     be future-compatible, we use the memset + min construction */
+  memset (&wtid,
+         0,
+         sizeof (wtid));
+  memcpy (&wtid,
+         reserve_pub,
+         GNUNET_MIN (sizeof (wtid),
+                     sizeof (*reserve_pub)));
+  iret = db_plugin->insert_reserve_closed (db_plugin->cls,
+                                          session,
+                                          reserve_pub,
+                                          now,
+                                          account_details,
+                                          &wtid,
+                                          left,
+                                          closing_fee);
+  if ( (GNUNET_OK == ret) &&
+       (GNUNET_OK == iret) )
+  {
+    /* success, perform wire transfer */
+    if (GNUNET_SYSERR ==
+       wp->wire_plugin->amount_round (wp->wire_plugin->cls,
+                                      &amount_without_fee))
+    {
+      GNUNET_break (0);
+      db_plugin->rollback (db_plugin->cls,
+                          session);
+      global_ret = GNUNET_SYSERR;
+      GNUNET_SCHEDULER_shutdown ();
+      return GNUNET_SYSERR;
+    }
+    ctc = GNUNET_new (struct CloseTransferContext);
+    ctc->session = session;
+    ctc->type = GNUNET_strdup (type);
+    ctc->ph
+      = wp->wire_plugin->prepare_wire_transfer (wp->wire_plugin->cls,
+                                               au->wire,
+                                               &amount_without_fee,
+                                               exchange_base_url,
+                                               &wtid,
+                                               &prepare_close_cb,
+                                               ctc);
+    if (NULL == ctc->ph)
+    {
+      GNUNET_break (0);
+      db_plugin->rollback (db_plugin->cls,
+                          session);
+      global_ret = GNUNET_SYSERR;
+      GNUNET_SCHEDULER_shutdown ();
+      GNUNET_free (ctc->type);
+      GNUNET_free (ctc);
+      ctc = NULL;
+    }
+    return GNUNET_SYSERR;
+  }
+  /* Check for hard failure */
+  if (GNUNET_SYSERR == iret)
+  {
+    GNUNET_break (0);
+    db_plugin->rollback (db_plugin->cls,
+                         session);
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    return GNUNET_SYSERR;
+  }
+  /* Reserve balance was almost zero; just commit */
+  if (GNUNET_OK !=
+      db_plugin->commit (db_plugin->cls,
+                        session))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               "Failed to commit database transaction!\n");
+  }
+  task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
+                                  NULL);
+  return GNUNET_SYSERR;
+}
+
+
+/**
+ * Main work function that finds and triggers transfers for reserves
+ * closures.
+ *
+ * @param cls closure
+ */
+static void
+run_reserve_closures (void *cls)
+{
+  struct TALER_EXCHANGEDB_Session *session;
+  int ret;
+  const struct GNUNET_SCHEDULER_TaskContext *tc;
+  
+  task = NULL;
+  reserves_idle = GNUNET_NO;
+  tc = GNUNET_SCHEDULER_get_task_context ();
+  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+    return;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Checking for reserves to close\n");
+  if (NULL == (session = db_plugin->get_session (db_plugin->cls)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to obtain database session!\n");
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  if (GNUNET_OK !=
+      db_plugin->start (db_plugin->cls,
+                       session))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to start database transaction!\n");
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  ret = db_plugin->get_expired_reserves (db_plugin->cls,
+                                        session,
+                                        GNUNET_TIME_absolute_get (),
+                                        &expired_reserve_cb,
+                                        session);
+  if (GNUNET_SYSERR == ret)
+  {
+    GNUNET_break (0);
+    db_plugin->rollback (db_plugin->cls,
+                         session);
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  if (GNUNET_NO == ret)
+  {
+    reserves_idle = GNUNET_YES;
+    db_plugin->rollback (db_plugin->cls,
+                         session);
+    task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+                                    NULL);
+    return;
+  }
+}
+
+
+/**
  * Main work function that queries the DB and aggregates transactions
  * into larger wire transfers.
  *
@@ -687,6 +1034,7 @@ prepare_cb (void *cls,
 static void
 run_aggregation (void *cls)
 {
+  static int swap;
   struct TALER_EXCHANGEDB_Session *session;
   unsigned int i;
   int ret;
@@ -696,6 +1044,12 @@ run_aggregation (void *cls)
   tc = GNUNET_SCHEDULER_get_task_context ();
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
+ if (0 == (++swap % 2))
+  {
+    task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
+                                    NULL);
+    return;
+  }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Checking for ready deposits to aggregate\n");
   if (NULL == (session = db_plugin->get_session (db_plugin->cls)))
@@ -748,9 +1102,13 @@ run_aggregation (void *cls)
     else
     {
       /* nothing to do, sleep for a minute and try again */
-      task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
-                                           &run_aggregation,
-                                           NULL);
+      if (GNUNET_NO == reserves_idle)
+       task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
+                                        NULL);
+      else
+       task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
+                                            &run_aggregation,
+                                            NULL);
     }
     return;
   }
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index 1de383e..0d74064 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -886,17 +886,16 @@ compile_reserve_history (const struct 
TALER_EXCHANGEDB_ReserveHistory *rh,
        rcc.reserve_pub = pos->details.closing->reserve_pub;
        TALER_JSON_hash (pos->details.closing->receiver_account_details,
                         &rcc.h_wire);
-       TALER_JSON_hash (pos->details.closing->transfer_details,
-                        &rcc.h_transfer);
+       rcc.wtid = pos->details.closing->transfer_details;
        TEH_KS_sign (&rcc.purpose,
                     &pub,
                     &sig);
        GNUNET_assert (0 ==
                       json_array_append_new (json_history,
-                                             json_pack ("{s:s, s:O, s:O, s:o, 
s:o, s:o, s:o, s:o}",
+                                             json_pack ("{s:s, s:O, s:o, s:o, 
s:o, s:o, s:o, s:o}",
                                                         "type", "CLOSING",
                                                         
"receiver_account_details", pos->details.closing->receiver_account_details,
-                                                        "transfer_details", 
pos->details.closing->transfer_details,
+                                                        "transfer_details", 
GNUNET_JSON_from_data_auto (&pos->details.closing->transfer_details),
                                                         "exchange_pub", 
GNUNET_JSON_from_data_auto (&pub),
                                                         "exchange_sig", 
GNUNET_JSON_from_data_auto (&sig),
                                                         "timestamp", 
GNUNET_JSON_from_time_abs (pos->details.closing->execution_date),
diff --git a/src/exchange/test-taler-exchange-aggregator-postgres.conf 
b/src/exchange/test-taler-exchange-aggregator-postgres.conf
index a5ee91a..00736e4 100644
--- a/src/exchange/test-taler-exchange-aggregator-postgres.conf
+++ b/src/exchange/test-taler-exchange-aggregator-postgres.conf
@@ -19,6 +19,15 @@ MASTER_PUBLIC_KEY = 
98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
 # Expected base URL of the exchange.
 BASE_URL = "https://exchange.taler.net/";
 
+[exchangedb]
+# After how long do we close idle reserves?  The exchange
+# and the auditor must agree on this value.  We currently
+# expect it to be globally defined for the whole system,
+# as there is no way for wallets to query this value.  Thus,
+# it is only configurable for testing, and should be treated
+# as constant in production.
+IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
+
 [exchangedb-postgres]
 
 #The connection string the plugin has to use for connecting to the database
diff --git a/src/exchange/test_taler_exchange_httpd.conf 
b/src/exchange/test_taler_exchange_httpd.conf
index 945031d..5f28271 100644
--- a/src/exchange/test_taler_exchange_httpd.conf
+++ b/src/exchange/test_taler_exchange_httpd.conf
@@ -19,6 +19,16 @@ MASTER_PUBLIC_KEY = 
98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
 DB = postgres
 
 
+[exchangedb]
+# After how long do we close idle reserves?  The exchange
+# and the auditor must agree on this value.  We currently
+# expect it to be globally defined for the whole system,
+# as there is no way for wallets to query this value.  Thus,
+# it is only configurable for testing, and should be treated
+# as constant in production.
+IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
+
+
 [exchangedb-postgres]
 DB_CONN_STR = "postgres:///talercheck"
 
diff --git a/src/exchangedb/exchangedb.conf b/src/exchangedb/exchangedb.conf
index 73e1603..7303025 100644
--- a/src/exchangedb/exchangedb.conf
+++ b/src/exchangedb/exchangedb.conf
@@ -12,3 +12,12 @@ AUDITOR_BASE_DIR = ${TALER_DATA_HOME}/auditors/
 # contain files "$METHOD.fee" with the cost structure, where
 # $METHOD corresponds to a wire transfer method.
 WIREFEE_BASE_DIR = ${TALER_DATA_HOME}/exchange/wirefees/
+
+
+# After how long do we close idle reserves?  The exchange
+# and the auditor must agree on this value.  We currently
+# expect it to be globally defined for the whole system,
+# as there is no way for wallets to query this value.  Thus,
+# it is only configurable for testing, and should be treated
+# as constant in production.
+IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
diff --git a/src/exchangedb/plugin_exchangedb_common.c 
b/src/exchangedb/plugin_exchangedb_common.c
index ba182d4..fac911d 100644
--- a/src/exchangedb/plugin_exchangedb_common.c
+++ b/src/exchangedb/plugin_exchangedb_common.c
@@ -64,8 +64,6 @@ common_free_reserve_history (void *cls,
       closing = rh->details.closing;
       if (NULL != closing->receiver_account_details)
         json_decref (closing->receiver_account_details);
-      if (NULL != closing->transfer_details)
-        json_decref (closing->transfer_details);
       GNUNET_free (closing);
       break;
     }
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 7ef6cef..35b24ed 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -141,6 +141,11 @@ struct PostgresClosure
    * the configuration.
    */
   char *connection_cfg_str;
+
+  /**
+   * After how long should idle reserves be closed?
+   */
+  struct GNUNET_TIME_Relative idle_reserve_expiration_time;
 };
 
 
@@ -316,9 +321,6 @@ postgres_create_tables (void *cls)
            ",fee_refund_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
            ")");
   /* denomination_revocations table is for remembering which denomination keys 
have been revoked */
-  /* TODO (#4981): change denom_pub_hash to REFERENCE 'denominations', and
-     add denom_pub_hash column to denominations, changing other REFERENCEs
-     also to the hash!? */
   SQLEXEC ("CREATE TABLE IF NOT EXISTS denomination_revocations"
            "(denom_revocations_serial_id BIGSERIAL"
           ",denom_pub_hash BYTEA PRIMARY KEY REFERENCES denominations 
(denom_pub_hash) ON DELETE CASCADE"
@@ -332,6 +334,7 @@ postgres_create_tables (void *cls)
      grabbing the money, depending on the Exchange's terms of service) */
   SQLEXEC ("CREATE TABLE IF NOT EXISTS reserves"
            "(reserve_pub BYTEA PRIMARY KEY CHECK(LENGTH(reserve_pub)=32)"
+          ",account_details TEXT NOT NULL "
            ",current_balance_val INT8 NOT NULL"
            ",current_balance_frac INT4 NOT NULL"
            ",current_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
@@ -367,7 +370,7 @@ postgres_create_tables (void *cls)
           "(close_uuid BIGSERIAL PRIMARY KEY"
           ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON 
DELETE CASCADE"
          ",execution_date INT8 NOT NULL"
-          ",transfer_details TEXT NOT NULL"
+         ",transfer_details BYTEA NOT NULL CHECK (LENGTH(transfer_details)=32)"
           ",receiver_account TEXT NOT NULL"
           ",amount_val INT8 NOT NULL"
           ",amount_frac INT4 NOT NULL"
@@ -715,13 +718,14 @@ postgres_prepare (PGconn *db_conn)
   PREPARE ("reserve_create",
            "INSERT INTO reserves "
            "(reserve_pub"
+          ",account_details"
            ",current_balance_val"
            ",current_balance_frac"
            ",current_balance_curr"
            ",expiration_date"
            ") VALUES "
-           "($1, $2, $3, $4, $5);",
-           5, NULL);
+           "($1, $2, $3, $4, $5, $6);",
+           6, NULL);
 
   /* Used in #postgres_insert_reserve_closed() */
   PREPARE ("reserves_close_insert",
@@ -1581,7 +1585,22 @@ postgres_prepare (PGconn *db_conn)
            " FROM reserves_close"
            " WHERE reserve_pub=$1;",
            1, NULL);
-  
+
+  /* Used in #postgres_get_expired_reserves() */
+  PREPARE ("get_expired_reserves",
+           "SELECT"
+          " expiration_date"
+          ",account_details"
+          ",reserve_pub"
+           ",current_balance_val"
+           ",current_balance_frac"
+           ",current_balance_curr"
+           " FROM reserves"
+           " WHERE expiration_date<=$1"
+          " AND (current_balance_val != 0 "
+          "      OR current_balance_frac != 0);",
+           1, NULL);
+
   /* Used in #postgres_get_coin_transactions() to obtain payback transactions
      for a coin */
   PREPARE ("payback_by_coin",
@@ -2069,6 +2088,7 @@ postgres_reserves_in_insert (void *cls,
                              const json_t *sender_account_details,
                              const json_t *transfer_details)
 {
+  struct PostgresClosure *pg = cls;
   PGresult *result;
   int reserve_exists;
   struct TALER_EXCHANGEDB_Reserve reserve;
@@ -2090,8 +2110,26 @@ postgres_reserves_in_insert (void *cls,
     GNUNET_break (0);
     goto rollback;
   }
+  if ( (0 == reserve.balance.value) &&
+       (0 == reserve.balance.fraction) )
+  {
+    /* TODO: reserve balance is empty, we might want to update
+       sender_account_details here.  (So that IF a customer uses the
+       same reserve public key from a different account, we USUALLY
+       switch to the new account (but only if the old reserve was
+       drained).)  This helps make sure that on reserve expiration the
+       funds go back to a valid account in cases where the customer
+       has closed the old bank account and some (buggy?) wallet keeps
+       using the same reserve key with the customer's new account.
+
+       Note that for a non-drained reserve we should not switch,
+       as that opens an attack vector for an adversary who can see
+       the wire transfer subjects (i.e. when using Bitcoin).
+    */
+  }
+  
   expiry = GNUNET_TIME_absolute_add (execution_time,
-                                     TALER_IDLE_RESERVE_EXPIRATION_TIME);
+                                     pg->idle_reserve_expiration_time);
   if (GNUNET_NO == reserve_exists)
   {
     /* New reserve, create balance for the first time; we do this
@@ -2101,6 +2139,7 @@ postgres_reserves_in_insert (void *cls,
        as a foreign key. */
     struct GNUNET_PQ_QueryParam params[] = {
       GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+      TALER_PQ_query_param_json (sender_account_details),
       TALER_PQ_query_param_amount (balance),
       GNUNET_PQ_query_param_absolute_time (&expiry),
       GNUNET_PQ_query_param_end
@@ -2302,6 +2341,7 @@ postgres_insert_withdraw_info (void *cls,
                                struct TALER_EXCHANGEDB_Session *session,
                                const struct 
TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
 {
+  struct PostgresClosure *pg = cls;
   PGresult *result;
   struct TALER_EXCHANGEDB_Reserve reserve;
   struct GNUNET_HashCode denom_pub_hash;
@@ -2356,7 +2396,7 @@ postgres_insert_withdraw_info (void *cls,
     return GNUNET_NO;
   }
   expiry = GNUNET_TIME_absolute_add (now,
-                                     TALER_IDLE_RESERVE_EXPIRATION_TIME);
+                                     pg->idle_reserve_expiration_time);
   reserve.expiry = GNUNET_TIME_absolute_max (expiry,
                                              reserve.expiry);
   if (GNUNET_OK != reserves_update (cls,
@@ -2618,8 +2658,8 @@ postgres_get_reserve_history (void *cls,
                                                &closing->execution_date),
           TALER_PQ_result_spec_json ("receiver_account",
                                     &closing->receiver_account_details),
-          TALER_PQ_result_spec_json ("transfer_details",
-                                    &closing->transfer_details),
+          GNUNET_PQ_result_spec_auto_from_type ("transfer_details",
+                                               &closing->transfer_details),
           GNUNET_PQ_result_spec_end
         };
         if (GNUNET_OK !=
@@ -4935,6 +4975,93 @@ postgres_insert_wire_fee (void *cls,
 
 
 /**
+ * Obtain information about expired reserves and their
+ * remaining balances.
+ *
+ * @param cls closure of the plugin
+ * @param session database connection
+ * @param now timestamp based on which we decide expiration
+ * @param rec function to call on expired reserves
+ * @param rec_cls closure for @a rec
+ * @return #GNUNET_SYSERR on database error
+ *         #GNUNET_NO if there are no expired non-empty reserves
+ *         #GNUNET_OK on success
+ */
+static int
+postgres_get_expired_reserves (void *cls,
+                              struct TALER_EXCHANGEDB_Session *session,
+                              struct GNUNET_TIME_Absolute now,
+                              TALER_EXCHANGEDB_ReserveExpiredCallback rec,
+                              void *rec_cls)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_absolute_time (&now),
+    GNUNET_PQ_query_param_end
+  };
+  PGresult *result;
+  int nrows;
+
+  result = GNUNET_PQ_exec_prepared (session->conn,
+                                    "get_expired_reserves",
+                                    params);
+  if (PGRES_TUPLES_OK !=
+      PQresultStatus (result))
+  {
+    BREAK_DB_ERR (result, session->conn);
+    PQclear (result);
+    return GNUNET_SYSERR;
+  }
+  nrows = PQntuples (result);
+  if (0 == nrows)
+  {
+    /* no matches found */
+    PQclear (result);
+    return GNUNET_NO;
+  }
+
+  for (int i=0;i<nrows;i++)
+  {
+    struct GNUNET_TIME_Absolute exp_date;
+    json_t *account_details;
+    struct TALER_ReservePublicKeyP reserve_pub;
+    struct TALER_Amount remaining_balance;
+    int ret;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_absolute_time ("expiration_date",
+                                          &exp_date),
+      TALER_PQ_result_spec_json ("account_details",
+                                &account_details),
+      GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
+                                           &reserve_pub),
+      TALER_PQ_result_spec_amount ("current_balance",
+                                  &remaining_balance),
+      GNUNET_PQ_result_spec_end
+    };
+  
+    if (GNUNET_OK !=
+       GNUNET_PQ_extract_result (result,
+                                 rs,
+                                 i))
+    {
+      PQclear (result);
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    ret = rec (rec_cls,
+              &reserve_pub,
+              &remaining_balance,
+              account_details,
+              exp_date);
+    GNUNET_PQ_cleanup_result (rs);
+    if (GNUNET_OK != ret)
+      break;
+  }
+  PQclear (result);
+  return GNUNET_OK;
+}
+
+
+/**
  * Insert reserve close operation into database.
  *
  * @param cls closure
@@ -4951,24 +5078,26 @@ postgres_insert_wire_fee (void *cls,
 static int
 postgres_insert_reserve_closed (void *cls,
                                struct TALER_EXCHANGEDB_Session *session,
-                               struct TALER_ReservePublicKeyP *reserve_pub,
+                               const struct TALER_ReservePublicKeyP 
*reserve_pub,
                                struct GNUNET_TIME_Absolute execution_date,
                                const json_t *receiver_account,
-                               const json_t *transfer_details,
+                               const struct TALER_WireTransferIdentifierRawP 
*transfer_details,
                                const struct TALER_Amount *amount_with_fee,
                                const struct TALER_Amount *closing_fee)
 {
+  struct TALER_EXCHANGEDB_Reserve reserve;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
     GNUNET_PQ_query_param_absolute_time (&execution_date),
-    TALER_PQ_query_param_json (transfer_details),
+    GNUNET_PQ_query_param_auto_from_type (transfer_details),
     TALER_PQ_query_param_json (receiver_account),
     TALER_PQ_query_param_amount (amount_with_fee),
     TALER_PQ_query_param_amount (closing_fee),
     GNUNET_PQ_query_param_end
   };
   PGresult *result;
-
+  int ret;
+  
   result = GNUNET_PQ_exec_prepared (session->conn,
                                    "reserves_close_insert",
                                    params);
@@ -4985,6 +5114,38 @@ postgres_insert_reserve_closed (void *cls,
     return GNUNET_SYSERR;
   }
   PQclear (result);
+
+  /* update reserve balance */
+  reserve.pub = *reserve_pub;
+  if (GNUNET_OK != postgres_reserve_get (cls,
+                                         session,
+                                         &reserve))
+  {
+    /* Should have been checked before we got here... */
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  ret = TALER_amount_subtract (&reserve.balance,
+                              &reserve.balance,
+                              amount_with_fee);
+  if (GNUNET_SYSERR == ret)
+  {
+    /* The reserve history was checked to make sure there is enough of a 
balance
+       left before we tried this; however, concurrent operations may have 
changed
+       the situation by now.  We should re-try the transaction.  */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Closing of reserve `%s' refused due to balance missmatch. 
Retrying.\n",
+                TALER_B2S (reserve_pub));
+    return GNUNET_NO;
+  }
+  GNUNET_break (GNUNET_NO == ret);
+  if (GNUNET_OK != reserves_update (cls,
+                                    session,
+                                    &reserve))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
   return GNUNET_OK;
 }
 
@@ -6069,7 +6230,7 @@ postgres_select_reserve_closed_above_serial_id (void *cls,
     uint64_t rowid;
     struct TALER_ReservePublicKeyP reserve_pub;
     json_t *receiver_account;
-    json_t *transfer_details;
+    struct TALER_WireTransferIdentifierRawP wtid;
     struct TALER_Amount amount_with_fee;
     struct TALER_Amount closing_fee;
     struct GNUNET_TIME_Absolute execution_date;
@@ -6080,8 +6241,8 @@ postgres_select_reserve_closed_above_serial_id (void *cls,
                                             &reserve_pub),
       GNUNET_PQ_result_spec_absolute_time ("execution_date",
                                            &execution_date),
-      TALER_PQ_result_spec_json ("transfer_details",
-                                &transfer_details),
+      GNUNET_PQ_result_spec_auto_from_type ("transfer_details",
+                                           &wtid),
       TALER_PQ_result_spec_json ("receiver_account",
                                 &receiver_account),
       TALER_PQ_result_spec_amount ("amount",
@@ -6107,7 +6268,7 @@ postgres_select_reserve_closed_above_serial_id (void *cls,
              &closing_fee,
               &reserve_pub,
              receiver_account,
-              transfer_details);
+              &wtid);
     GNUNET_PQ_cleanup_result (rs);
     if (GNUNET_OK != ret)
       break;
@@ -6147,6 +6308,7 @@ postgres_insert_payback_request (void *cls,
                                  const struct GNUNET_HashCode *h_blind_ev,
                                  struct GNUNET_TIME_Absolute timestamp)
 {
+  struct PostgresClosure *pg = cls;
   PGresult *result;
   struct GNUNET_TIME_Absolute expiry;
   struct TALER_EXCHANGEDB_Reserve reserve;
@@ -6215,7 +6377,7 @@ postgres_insert_payback_request (void *cls,
     return GNUNET_SYSERR;
   }
   expiry = GNUNET_TIME_absolute_add (timestamp,
-                                     TALER_IDLE_RESERVE_EXPIRATION_TIME);
+                                     pg->idle_reserve_expiration_time);
   reserve.expiry = GNUNET_TIME_absolute_max (expiry,
                                              reserve.expiry);
   if (GNUNET_OK != reserves_update (cls,
@@ -6464,6 +6626,18 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
       return NULL;
     }
   }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_time (cfg,
+                                          "exchangedb",
+                                          "IDLE_RESERVE_EXPIRATION_TIME",
+                                          &pg->idle_reserve_expiration_time))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "exchangedb",
+                               "IDLE_RESERVE_EXPIRATION_TIME");
+    GNUNET_free (pg);
+    return NULL;
+  }
   plugin = GNUNET_new (struct TALER_EXCHANGEDB_Plugin);
   plugin->cls = pg;
   plugin->get_session = &postgres_get_session;
@@ -6509,6 +6683,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
   plugin->insert_aggregation_tracking = &postgres_insert_aggregation_tracking;
   plugin->insert_wire_fee = &postgres_insert_wire_fee;
   plugin->get_wire_fee = &postgres_get_wire_fee;
+  plugin->get_expired_reserves = &postgres_get_expired_reserves;
   plugin->insert_reserve_closed = &postgres_insert_reserve_closed;
   plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert;
   plugin->wire_prepare_data_mark_finished = 
&postgres_wire_prepare_data_mark_finished;
diff --git a/src/exchangedb/test-exchange-db-postgres.conf 
b/src/exchangedb/test-exchange-db-postgres.conf
index 0822bab..926e299 100644
--- a/src/exchangedb/test-exchange-db-postgres.conf
+++ b/src/exchangedb/test-exchange-db-postgres.conf
@@ -6,3 +6,14 @@ DB = postgres
 
 #The connection string the plugin has to use for connecting to the database
 DB_CONN_STR = postgres:///talercheck
+
+
+[exchangedb]
+
+# After how long do we close idle reserves?  The exchange
+# and the auditor must agree on this value.  We currently
+# expect it to be globally defined for the whole system,
+# as there is no way for wallets to query this value.  Thus,
+# it is only configurable for testing, and should be treated
+# as constant in production.
+IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index 83949d8..341d31f 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -1612,24 +1612,37 @@ run (void *cls)
                                           &value,
                                           &cbc.h_coin_envelope,
                                           deadline));
+  FAILIF (GNUNET_OK !=
+          plugin->select_payback_above_serial_id (plugin->cls,
+                                                  session,
+                                                  0,
+                                                  &payback_cb,
+                                                  &coin_blind));
+
+  GNUNET_assert (GNUNET_OK ==
+                TALER_amount_add (&amount_with_fee,
+                                  &value,
+                                  &value));
   sndr = json_loads ("{ \"account\":\"1\" }", 0, NULL);
-  just = json_loads ("{ \"trans-details\":\"2\" }", 0, NULL);
   GNUNET_assert (GNUNET_OK ==
                  TALER_string_to_amount (CURRENCY ":0.000010",
                                          &fee_closing));
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_string_to_amount (CURRENCY ":1.000010",
-                                         &amount_with_fee));
   FAILIF (GNUNET_OK !=
          plugin->insert_reserve_closed (plugin->cls,
                                         session,
                                         &reserve_pub,
                                         GNUNET_TIME_absolute_get (),
-                                        sndr /* receiver_account */,
-                                        just /* transfer_details */,
+                                        sndr,
+                                        &wire_out_wtid,
                                         &amount_with_fee,
                                         &fee_closing));
-  json_decref (just);
+  FAILIF (GNUNET_OK !=
+          check_reserve (session,
+                         &reserve_pub,
+                        0,
+                        0,
+                         value.currency));
+  
   json_decref (sndr);  
   result = 7;
   rh = plugin->get_reserve_history (plugin->cls,
@@ -1880,13 +1893,6 @@ run (void *cls)
                                           &cbc.h_coin_envelope,
                                           deadline));
 
-  FAILIF (GNUNET_OK !=
-          plugin->select_payback_above_serial_id (plugin->cls,
-                                                  session,
-                                                  0,
-                                                  &payback_cb,
-                                                  &coin_blind));
-
   auditor_row_cnt = 0;
   FAILIF (GNUNET_OK !=
           plugin->select_refunds_above_serial_id (plugin->cls,
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 15e51bc..d1d6f3b 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -765,7 +765,7 @@ struct TALER_EXCHANGE_ReserveHistory
       /**
        * Wire transfer details for the outgoing wire transfer.
        */
-      json_t *transfer_details;
+      struct TALER_WireTransferIdentifierRawP wtid;
 
       /**
        * Signature of the coin of type
diff --git a/src/include/taler_exchangedb_lib.h 
b/src/include/taler_exchangedb_lib.h
index 561738c..e4284c2 100644
--- a/src/include/taler_exchangedb_lib.h
+++ b/src/include/taler_exchangedb_lib.h
@@ -23,6 +23,7 @@
 #ifndef TALER_EXCHANGEDB_LIB_H
 #define TALER_EXCHANGEDB_LIB_H
 
+
 #include "taler_signatures.h"
 
 
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 5daa9d2..b040077 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -100,8 +100,8 @@ struct TALER_EXCHANGEDB_ClosingTransfer
    * Detailed wire transfer information that uniquely identifies the
    * wire transfer.
    */
-  json_t *transfer_details;
-
+  struct TALER_WireTransferIdentifierRawP transfer_details;
+  
 };
 
 
@@ -991,7 +991,25 @@ typedef int
                                          const struct TALER_Amount 
*closing_fee,
                                          const struct TALER_ReservePublicKeyP 
*reserve_pub,
                                          const json_t *receiver_account,
-                                         const json_t *transfer_details);
+                                         const struct 
TALER_WireTransferIdentifierRawP *transfer_details);
+
+
+/**
+ * Function called with details about expired reserves.
+ *
+ * @param cls closure
+ * @param reserve_pub public key of the reserve
+ * @param left amount left in the reserve
+ * @param account_details information about the reserve's bank account
+ * @param expiration_date when did the reserve expire
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ */
+typedef int
+(*TALER_EXCHANGEDB_ReserveExpiredCallback)(void *cls,
+                                          const struct TALER_ReservePublicKeyP 
*reserve_pub,
+                                          const struct TALER_Amount *left,
+                                          const json_t *account_details,
+                                          struct GNUNET_TIME_Absolute 
expiration_date);
 
 
 /**
@@ -1784,6 +1802,27 @@ struct TALER_EXCHANGEDB_Plugin
 
 
   /**
+   * Obtain information about expired reserves and their
+   * remaining balances.
+   *
+   * @param cls closure of the plugin
+   * @param session database connection
+   * @param now timestamp based on which we decide expiration
+   * @param rec function to call on expired reserves
+   * @param rec_cls closure for @a rec
+   * @return #GNUNET_SYSERR on database error
+   *         #GNUNET_NO if there are no expired non-empty reserves
+   *         #GNUNET_OK on success
+   */
+  int
+  (*get_expired_reserves)(void *cls,
+                         struct TALER_EXCHANGEDB_Session *session,
+                         struct GNUNET_TIME_Absolute now,
+                         TALER_EXCHANGEDB_ReserveExpiredCallback rec,
+                         void *rec_cls);
+
+  
+  /**
    * Insert reserve close operation into database.
    *
    * @param cls closure
@@ -1800,10 +1839,10 @@ struct TALER_EXCHANGEDB_Plugin
   int
   (*insert_reserve_closed)(void *cls,
                           struct TALER_EXCHANGEDB_Session *session,
-                          struct TALER_ReservePublicKeyP *reserve_pub,
+                          const struct TALER_ReservePublicKeyP *reserve_pub,
                           struct GNUNET_TIME_Absolute execution_date,
                           const json_t *receiver_account,
-                          const json_t *transfer_details,
+                          const struct TALER_WireTransferIdentifierRawP 
*transfer_details,
                           const struct TALER_Amount *amount_with_fee,
                           const struct TALER_Amount *closing_fee);
 
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
index de9a2f7..f460130 100644
--- a/src/include/taler_signatures.h
+++ b/src/include/taler_signatures.h
@@ -46,11 +46,6 @@
  */
 #define TALER_CNC_KAPPA 3
 
-/**
- * After what time do idle reserves "expire"?  We might want to make
- * this a configuration option (eventually).
- */
-#define TALER_IDLE_RESERVE_EXPIRATION_TIME GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_YEARS, 5)
 
 /*********************************************/
 /* Exchange offline signatures (with master key) */
@@ -1269,9 +1264,9 @@ struct TALER_ReserveCloseConfirmationPS
   struct GNUNET_HashCode h_wire;
 
   /**
-   * Hash of the transfer details.
+   * Wire transfer subject.
    */
-  struct GNUNET_HashCode h_transfer;
+  struct TALER_WireTransferIdentifierRawP wtid;
 };
 
 

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



reply via email to

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