gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated: -refactor DB for reserve history


From: gnunet
Subject: [taler-exchange] branch master updated: -refactor DB for reserve history/status routines
Date: Sat, 15 Oct 2022 16:19:18 +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 38876c50 -refactor DB for reserve history/status routines
38876c50 is described below

commit 38876c503ff53f7adf44bc82be4fec3ae9b02d01
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sat Oct 15 16:19:14 2022 +0200

    -refactor DB for reserve history/status routines
---
 contrib/gana                                  |    2 +-
 src/exchange/taler-exchange-httpd_responses.c |   56 ++
 src/exchangedb/Makefile.am                    |    1 +
 src/exchangedb/pg_get_reserve_history.c       | 1018 ++++++++++++++++++++++++
 src/exchangedb/pg_get_reserve_history.h       |   67 ++
 src/exchangedb/plugin_exchangedb_common.c     |   16 +
 src/exchangedb/plugin_exchangedb_postgres.c   | 1033 +------------------------
 src/include/taler_crypto_lib.h                |   49 +-
 src/include/taler_exchangedb_plugin.h         |   91 ++-
 src/util/exchange_signatures.c                |   94 +++
 src/util/wallet_signatures.c                  |    2 +-
 11 files changed, 1396 insertions(+), 1033 deletions(-)

diff --git a/contrib/gana b/contrib/gana
index affa835c..9657bf77 160000
--- a/contrib/gana
+++ b/contrib/gana
@@ -1 +1 @@
-Subproject commit affa835c8ee4789134bdb5b49928dd3788d847da
+Subproject commit 9657bf77de05c0ac17ff39629306a604066b21de
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index 77f01de6..2cef1719 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -425,6 +425,62 @@ TEH_RESPONSE_compile_transaction_history (
         break;
       }
 
+    case TALER_EXCHANGEDB_TT_PURSE_REFUND:
+      {
+        const struct TALER_EXCHANGEDB_PurseRefundListEntry *prefund =
+          pos->details.purse_refund;
+        struct TALER_Amount value;
+        enum TALER_ErrorCode ec;
+        struct TALER_ExchangePublicKeyP epub;
+        struct TALER_ExchangeSignatureP esig;
+
+        if (0 >
+            TALER_amount_subtract (&value,
+                                   &prefund->refund_amount,
+                                   &prefund->refund_fee))
+        {
+          GNUNET_break (0);
+          json_decref (history);
+          return NULL;
+        }
+        ec = TALER_exchange_online_purse_refund_sign (
+          &TEH_keys_exchange_sign_,
+          &value,
+          &prefund->refund_fee,
+          coin_pub,
+          &prefund->purse_pub,
+          &epub,
+          &esig);
+        if (TALER_EC_NONE != ec)
+        {
+          GNUNET_break (0);
+          json_decref (history);
+          return NULL;
+        }
+        if (0 !=
+            json_array_append_new (
+              history,
+              GNUNET_JSON_PACK (
+                GNUNET_JSON_pack_string ("type",
+                                         "PURSE-REFUND"),
+                TALER_JSON_pack_amount ("amount",
+                                        &value),
+                TALER_JSON_pack_amount ("refund_fee",
+                                        &prefund->refund_fee),
+                GNUNET_JSON_pack_data_auto ("exchange_sig",
+                                            &esig),
+                GNUNET_JSON_pack_data_auto ("exchange_pub",
+                                            &epub),
+                GNUNET_JSON_pack_data_auto ("purse_pub",
+                                            &prefund->purse_pub))))
+        {
+          GNUNET_break (0);
+          json_decref (history);
+          return NULL;
+        }
+      }
+      break;
+
     case TALER_EXCHANGEDB_TT_RESERVE_OPEN:
       {
         struct TALER_EXCHANGEDB_ReserveOpenListEntry *role
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index 059c0136..61b18d13 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -73,6 +73,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
   pg_do_reserve_open.c pg_do_reserve_open.h \
   pg_get_coin_transactions.c pg_get_coin_transactions.h \
   pg_get_expired_reserves.c pg_get_expired_reserves.h \
+  pg_get_reserve_history.c pg_get_reserve_history.h \
   pg_get_unfinished_close_requests.c pg_get_unfinished_close_requests.h \
   pg_insert_close_request.c pg_insert_close_request.h \
   pg_insert_records_by_table.c pg_insert_records_by_table.h \
diff --git a/src/exchangedb/pg_get_reserve_history.c 
b/src/exchangedb/pg_get_reserve_history.c
new file mode 100644
index 00000000..c3ccab1f
--- /dev/null
+++ b/src/exchangedb/pg_get_reserve_history.c
@@ -0,0 +1,1018 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 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 pg_get_reserve_history.c
+ * @brief Low-level (statement-level) Postgres database access for the exchange
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_get_reserve_history.h"
+#include "plugin_exchangedb_common.h"
+#include "pg_helper.h"
+
+/**
+ * Closure for callbacks invoked via #postgres_get_reserve_history.
+ */
+struct ReserveHistoryContext
+{
+
+  /**
+   * Which reserve are we building the history for?
+   */
+  const struct TALER_ReservePublicKeyP *reserve_pub;
+
+  /**
+   * Where we build the history.
+   */
+  struct TALER_EXCHANGEDB_ReserveHistory *rh;
+
+  /**
+   * Tail of @e rh list.
+   */
+  struct TALER_EXCHANGEDB_ReserveHistory *rh_tail;
+
+  /**
+   * Plugin context.
+   */
+  struct PostgresClosure *pg;
+
+  /**
+   * Sum of all credit transactions.
+   */
+  struct TALER_Amount balance_in;
+
+  /**
+   * Sum of all debit transactions.
+   */
+  struct TALER_Amount balance_out;
+
+  /**
+   * Set to #GNUNET_SYSERR on serious internal errors during
+   * the callbacks.
+   */
+  enum GNUNET_GenericReturnValue status;
+};
+
+
+/**
+ * Append and return a fresh element to the reserve
+ * history kept in @a rhc.
+ *
+ * @param rhc where the history is kept
+ * @return the fresh element that was added
+ */
+static struct TALER_EXCHANGEDB_ReserveHistory *
+append_rh (struct ReserveHistoryContext *rhc)
+{
+  struct TALER_EXCHANGEDB_ReserveHistory *tail;
+
+  tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory);
+  if (NULL != rhc->rh_tail)
+  {
+    rhc->rh_tail->next = tail;
+    rhc->rh_tail = tail;
+  }
+  else
+  {
+    rhc->rh_tail = tail;
+    rhc->rh = tail;
+  }
+  return tail;
+}
+
+
+/**
+ * Add bank transfers to result set for #postgres_get_reserve_history.
+ *
+ * @param cls a `struct ReserveHistoryContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+add_bank_to_exchange (void *cls,
+                      PGresult *result,
+                      unsigned int num_results)
+{
+  struct ReserveHistoryContext *rhc = cls;
+  struct PostgresClosure *pg = rhc->pg;
+
+  while (0 < num_results)
+  {
+    struct TALER_EXCHANGEDB_BankTransfer *bt;
+    struct TALER_EXCHANGEDB_ReserveHistory *tail;
+
+    bt = GNUNET_new (struct TALER_EXCHANGEDB_BankTransfer);
+    {
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        GNUNET_PQ_result_spec_uint64 ("wire_reference",
+                                      &bt->wire_reference),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("credit",
+                                     &bt->amount),
+        GNUNET_PQ_result_spec_timestamp ("execution_date",
+                                         &bt->execution_date),
+        GNUNET_PQ_result_spec_string ("sender_account_details",
+                                      &bt->sender_account_details),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    --num_results))
+      {
+        GNUNET_break (0);
+        GNUNET_free (bt);
+        rhc->status = GNUNET_SYSERR;
+        return;
+      }
+    }
+    GNUNET_assert (0 <=
+                   TALER_amount_add (&rhc->balance_in,
+                                     &rhc->balance_in,
+                                     &bt->amount));
+    bt->reserve_pub = *rhc->reserve_pub;
+    tail = append_rh (rhc);
+    tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE;
+    tail->details.bank = bt;
+  } /* end of 'while (0 < rows)' */
+}
+
+
+/**
+ * Add coin withdrawals to result set for #postgres_get_reserve_history.
+ *
+ * @param cls a `struct ReserveHistoryContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+add_withdraw_coin (void *cls,
+                   PGresult *result,
+                   unsigned int num_results)
+{
+  struct ReserveHistoryContext *rhc = cls;
+  struct PostgresClosure *pg = rhc->pg;
+
+  while (0 < num_results)
+  {
+    struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc;
+    struct TALER_EXCHANGEDB_ReserveHistory *tail;
+
+    cbc = GNUNET_new (struct TALER_EXCHANGEDB_CollectableBlindcoin);
+    {
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
+                                              &cbc->h_coin_envelope),
+        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
+                                              &cbc->denom_pub_hash),
+        TALER_PQ_result_spec_blinded_denom_sig ("denom_sig",
+                                                &cbc->sig),
+        GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
+                                              &cbc->reserve_sig),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
+                                     &cbc->amount_with_fee),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
+                                     &cbc->withdraw_fee),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    --num_results))
+      {
+        GNUNET_break (0);
+        GNUNET_free (cbc);
+        rhc->status = GNUNET_SYSERR;
+        return;
+      }
+    }
+    GNUNET_assert (0 <=
+                   TALER_amount_add (&rhc->balance_out,
+                                     &rhc->balance_out,
+                                     &cbc->amount_with_fee));
+    cbc->reserve_pub = *rhc->reserve_pub;
+    tail = append_rh (rhc);
+    tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN;
+    tail->details.withdraw = cbc;
+  }
+}
+
+
+/**
+ * Add recoups to result set for #postgres_get_reserve_history.
+ *
+ * @param cls a `struct ReserveHistoryContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+add_recoup (void *cls,
+            PGresult *result,
+            unsigned int num_results)
+{
+  struct ReserveHistoryContext *rhc = cls;
+  struct PostgresClosure *pg = rhc->pg;
+
+  while (0 < num_results)
+  {
+    struct TALER_EXCHANGEDB_Recoup *recoup;
+    struct TALER_EXCHANGEDB_ReserveHistory *tail;
+
+    recoup = GNUNET_new (struct TALER_EXCHANGEDB_Recoup);
+    {
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
+                                     &recoup->value),
+        GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
+                                              &recoup->coin.coin_pub),
+        GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
+                                              &recoup->coin_blind),
+        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+                                              &recoup->coin_sig),
+        GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
+                                         &recoup->timestamp),
+        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
+                                              &recoup->coin.denom_pub_hash),
+        TALER_PQ_result_spec_denom_sig (
+          "denom_sig",
+          &recoup->coin.denom_sig),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    --num_results))
+      {
+        GNUNET_break (0);
+        GNUNET_free (recoup);
+        rhc->status = GNUNET_SYSERR;
+        return;
+      }
+    }
+    GNUNET_assert (0 <=
+                   TALER_amount_add (&rhc->balance_in,
+                                     &rhc->balance_in,
+                                     &recoup->value));
+    recoup->reserve_pub = *rhc->reserve_pub;
+    tail = append_rh (rhc);
+    tail->type = TALER_EXCHANGEDB_RO_RECOUP_COIN;
+    tail->details.recoup = recoup;
+  } /* end of 'while (0 < rows)' */
+}
+
+
+/**
+ * Add exchange-to-bank transfers to result set for
+ * #postgres_get_reserve_history.
+ *
+ * @param cls a `struct ReserveHistoryContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+add_exchange_to_bank (void *cls,
+                      PGresult *result,
+                      unsigned int num_results)
+{
+  struct ReserveHistoryContext *rhc = cls;
+  struct PostgresClosure *pg = rhc->pg;
+
+  while (0 < num_results)
+  {
+    struct TALER_EXCHANGEDB_ClosingTransfer *closing;
+    struct TALER_EXCHANGEDB_ReserveHistory *tail;
+
+    closing = GNUNET_new (struct TALER_EXCHANGEDB_ClosingTransfer);
+    {
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
+                                     &closing->amount),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
+                                     &closing->closing_fee),
+        GNUNET_PQ_result_spec_timestamp ("execution_date",
+                                         &closing->execution_date),
+        GNUNET_PQ_result_spec_string ("receiver_account",
+                                      &closing->receiver_account_details),
+        GNUNET_PQ_result_spec_auto_from_type ("wtid",
+                                              &closing->wtid),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    --num_results))
+      {
+        GNUNET_break (0);
+        GNUNET_free (closing);
+        rhc->status = GNUNET_SYSERR;
+        return;
+      }
+    }
+    GNUNET_assert (0 <=
+                   TALER_amount_add (&rhc->balance_out,
+                                     &rhc->balance_out,
+                                     &closing->amount));
+    closing->reserve_pub = *rhc->reserve_pub;
+    tail = append_rh (rhc);
+    tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK;
+    tail->details.closing = closing;
+  } /* end of 'while (0 < rows)' */
+}
+
+
+/**
+ * Add purse merge transfers to result set for
+ * #postgres_get_reserve_history.
+ *
+ * @param cls a `struct ReserveHistoryContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+add_p2p_merge (void *cls,
+               PGresult *result,
+               unsigned int num_results)
+{
+  struct ReserveHistoryContext *rhc = cls;
+  struct PostgresClosure *pg = rhc->pg;
+
+  while (0 < num_results)
+  {
+    struct TALER_EXCHANGEDB_PurseMerge *merge;
+    struct TALER_EXCHANGEDB_ReserveHistory *tail;
+
+    merge = GNUNET_new (struct TALER_EXCHANGEDB_PurseMerge);
+    {
+      uint32_t flags32;
+      struct TALER_Amount balance;
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee",
+                                     &merge->purse_fee),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
+                                     &balance),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
+                                     &merge->amount_with_fee),
+        GNUNET_PQ_result_spec_timestamp ("merge_timestamp",
+                                         &merge->merge_timestamp),
+        GNUNET_PQ_result_spec_timestamp ("purse_expiration",
+                                         &merge->purse_expiration),
+        GNUNET_PQ_result_spec_uint32 ("age_limit",
+                                      &merge->min_age),
+        GNUNET_PQ_result_spec_uint32 ("flags",
+                                      &flags32),
+        GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+                                              &merge->h_contract_terms),
+        GNUNET_PQ_result_spec_auto_from_type ("merge_pub",
+                                              &merge->merge_pub),
+        GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
+                                              &merge->purse_pub),
+        GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
+                                              &merge->reserve_sig),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    --num_results))
+      {
+        GNUNET_break (0);
+        GNUNET_free (merge);
+        rhc->status = GNUNET_SYSERR;
+        return;
+      }
+      merge->flags = (enum TALER_WalletAccountMergeFlags) flags32;
+      if ( (! GNUNET_TIME_absolute_is_future (
+              merge->merge_timestamp.abs_time)) &&
+           (-1 != TALER_amount_cmp (&balance,
+                                    &merge->amount_with_fee)) )
+        merge->merged = true;
+    }
+    if (merge->merged)
+      GNUNET_assert (0 <=
+                     TALER_amount_add (&rhc->balance_in,
+                                       &rhc->balance_in,
+                                       &merge->amount_with_fee));
+    GNUNET_assert (0 <=
+                   TALER_amount_add (&rhc->balance_out,
+                                     &rhc->balance_out,
+                                     &merge->purse_fee));
+    merge->reserve_pub = *rhc->reserve_pub;
+    tail = append_rh (rhc);
+    tail->type = TALER_EXCHANGEDB_RO_PURSE_MERGE;
+    tail->details.merge = merge;
+  }
+}
+
+
+/**
+ * Add paid for history requests to result set for
+ * #postgres_get_reserve_history.
+ *
+ * @param cls a `struct ReserveHistoryContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+add_history_requests (void *cls,
+                      PGresult *result,
+                      unsigned int num_results)
+{
+  struct ReserveHistoryContext *rhc = cls;
+  struct PostgresClosure *pg = rhc->pg;
+
+  while (0 < num_results)
+  {
+    struct TALER_EXCHANGEDB_HistoryRequest *history;
+    struct TALER_EXCHANGEDB_ReserveHistory *tail;
+
+    history = GNUNET_new (struct TALER_EXCHANGEDB_HistoryRequest);
+    {
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
+                                     &history->history_fee),
+        GNUNET_PQ_result_spec_timestamp ("request_timestamp",
+                                         &history->request_timestamp),
+        GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
+                                              &history->reserve_sig),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    --num_results))
+      {
+        GNUNET_break (0);
+        GNUNET_free (history);
+        rhc->status = GNUNET_SYSERR;
+        return;
+      }
+    }
+    GNUNET_assert (0 <=
+                   TALER_amount_add (&rhc->balance_out,
+                                     &rhc->balance_out,
+                                     &history->history_fee));
+    history->reserve_pub = *rhc->reserve_pub;
+    tail = append_rh (rhc);
+    tail->type = TALER_EXCHANGEDB_RO_HISTORY_REQUEST;
+    tail->details.history = history;
+  }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_reserve_history (void *cls,
+                            const struct TALER_ReservePublicKeyP *reserve_pub,
+                            struct TALER_Amount *balance,
+                            struct TALER_EXCHANGEDB_ReserveHistory **rhp)
+{
+  struct PostgresClosure *pg = cls;
+  struct ReserveHistoryContext rhc;
+  struct
+  {
+    /**
+     * Name of the prepared statement to run.
+     */
+    const char *statement;
+    /**
+     * Function to use to process the results.
+     */
+    GNUNET_PQ_PostgresResultHandler cb;
+  } work[] = {
+    /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */
+    { "reserves_in_get_transactions",
+      add_bank_to_exchange },
+    /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */
+    { "get_reserves_out",
+      &add_withdraw_coin },
+    /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */
+    { "recoup_by_reserve",
+      &add_recoup },
+    /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */
+    { "close_by_reserve",
+      &add_exchange_to_bank },
+    /** #TALER_EXCHANGEDB_RO_PURSE_MERGE */
+    { "merge_by_reserve",
+      &add_p2p_merge },
+    /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */
+    { "history_by_reserve",
+      &add_history_requests },
+    /* List terminator */
+    { NULL,
+      NULL }
+  };
+  enum GNUNET_DB_QueryStatus qs;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+    GNUNET_PQ_query_param_end
+  };
+
+  PREPARE (pg,
+           "reserves_in_get_transactions",
+           /*
+           "SELECT"
+           " wire_reference"
+           ",credit_val"
+           ",credit_frac"
+           ",execution_date"
+           ",payto_uri AS sender_account_details"
+           " FROM reserves_in"
+           " JOIN wire_targets"
+           "   ON (wire_source_h_payto = wire_target_h_payto)"
+           " WHERE reserve_pub=$1;",
+           */
+           "WITH ri AS MATERIALIZED ( "
+           "  SELECT * "
+           "  FROM reserves_in "
+           "  WHERE reserve_pub = $1 "
+           ") "
+           "SELECT  "
+           "  wire_reference "
+           "  ,credit_val "
+           "  ,credit_frac "
+           "  ,execution_date "
+           "  ,payto_uri AS sender_account_details "
+           "FROM wire_targets "
+           "JOIN ri  "
+           "  ON (wire_target_h_payto = wire_source_h_payto) "
+           "WHERE wire_target_h_payto = ( "
+           "  SELECT wire_source_h_payto FROM ri "
+           "); ");
+
+  PREPARE (pg,
+           "get_reserves_out",
+           /*
+           "SELECT"
+           " ro.h_blind_ev"
+           ",denom.denom_pub_hash"
+           ",ro.denom_sig"
+           ",ro.reserve_sig"
+           ",ro.execution_date"
+           ",ro.amount_with_fee_val"
+           ",ro.amount_with_fee_frac"
+           ",denom.fee_withdraw_val"
+           ",denom.fee_withdraw_frac"
+           " FROM reserves res"
+           " JOIN reserves_out_by_reserve ror"
+           "   ON (res.reserve_uuid = ror.reserve_uuid)"
+           " JOIN reserves_out ro"
+           "   ON (ro.h_blind_ev = ror.h_blind_ev)"
+           " JOIN denominations denom"
+           "   ON (ro.denominations_serial = denom.denominations_serial)"
+           " WHERE res.reserve_pub=$1;",
+           */
+           "WITH robr AS MATERIALIZED ( "
+           "  SELECT h_blind_ev "
+           "  FROM reserves_out_by_reserve "
+           "  WHERE reserve_uuid= ( "
+           "    SELECT reserve_uuid "
+           "    FROM reserves "
+           "    WHERE reserve_pub = $1 "
+           "  ) "
+           ") SELECT "
+           "  ro.h_blind_ev "
+           "  ,denom.denom_pub_hash "
+           "  ,ro.denom_sig "
+           "  ,ro.reserve_sig "
+           "  ,ro.execution_date "
+           "  ,ro.amount_with_fee_val "
+           "  ,ro.amount_with_fee_frac "
+           "  ,denom.fee_withdraw_val "
+           "  ,denom.fee_withdraw_frac "
+           "FROM robr "
+           "JOIN reserves_out ro "
+           "  ON (ro.h_blind_ev = robr.h_blind_ev) "
+           "JOIN denominations denom "
+           "  ON (ro.denominations_serial = denom.denominations_serial);");
+  PREPARE (pg,
+           "recoup_by_reserve",
+           /*
+           "SELECT"
+           " recoup.coin_pub"
+           ",recoup.coin_sig"
+           ",recoup.coin_blind"
+           ",recoup.amount_val"
+           ",recoup.amount_frac"
+           ",recoup.recoup_timestamp"
+           ",denominations.denom_pub_hash"
+           ",known_coins.denom_sig"
+           " FROM denominations"
+           " JOIN (known_coins"
+           "   JOIN recoup "
+           "   ON (recoup.coin_pub = known_coins.coin_pub))"
+           "  ON (known_coins.denominations_serial = 
denominations.denominations_serial)"
+           " WHERE recoup.coin_pub"
+           " IN (SELECT coin_pub"
+           "     FROM recoup_by_reserve"
+           "     JOIN (reserves_out"
+           "       JOIN (reserves_out_by_reserve"
+           "         JOIN reserves"
+           "           ON (reserves.reserve_uuid = 
reserves_out_by_reserve.reserve_uuid))"
+           "       ON (reserves_out_by_reserve.h_blind_ev = 
reserves_out.h_blind_ev))"
+           "     ON (recoup_by_reserve.reserve_out_serial_id = 
reserves_out.reserve_out_serial_id)"
+           "     WHERE reserves.reserve_pub=$1);",
+           */
+           "SELECT robr.coin_pub "
+           "  ,robr.coin_sig "
+           "  ,robr.coin_blind "
+           "  ,robr.amount_val "
+           "  ,robr.amount_frac "
+           "  ,robr.recoup_timestamp "
+           "  ,denominations.denom_pub_hash "
+           "  ,robr.denom_sig "
+           "FROM denominations "
+           "  JOIN exchange_do_recoup_by_reserve($1) robr"
+           " USING (denominations_serial);");
+
+  PREPARE (pg,
+           "close_by_reserve",
+           "SELECT"
+           " amount_val"
+           ",amount_frac"
+           ",closing_fee_val"
+           ",closing_fee_frac"
+           ",execution_date"
+           ",payto_uri AS receiver_account"
+           ",wtid"
+           " FROM reserves_close"
+           "   JOIN wire_targets"
+           "     USING (wire_target_h_payto)"
+           " WHERE reserve_pub=$1;");
+
+  PREPARE (pg,
+           "merge_by_reserve",
+           "SELECT"
+           " pr.amount_with_fee_val"
+           ",pr.amount_with_fee_frac"
+           ",pr.balance_val"
+           ",pr.balance_frac"
+           ",pr.purse_fee_val"
+           ",pr.purse_fee_frac"
+           ",pr.h_contract_terms"
+           ",pr.merge_pub"
+           ",am.reserve_sig"
+           ",pm.purse_pub"
+           ",pm.merge_timestamp"
+           ",pr.purse_expiration"
+           ",pr.age_limit"
+           ",pr.flags"
+           " FROM purse_merges pm"
+           "   JOIN purse_requests pr"
+           "     USING (purse_pub)"
+           "   JOIN account_merges am"
+           "     ON (am.purse_pub = pm.purse_pub AND"
+           "         am.reserve_pub = pm.reserve_pub)"
+           " WHERE pm.reserve_pub=$1"
+           "  AND pm.partner_serial_id=0" /* must be local! */
+           "  AND pr.finished"
+           "  AND NOT pr.refunded;");
+
+  PREPARE (pg,
+           "history_by_reserve",
+           "SELECT"
+           " history_fee_val"
+           ",history_fee_frac"
+           ",request_timestamp"
+           ",reserve_sig"
+           " FROM history_requests"
+           " WHERE reserve_pub=$1;");
+
+  rhc.reserve_pub = reserve_pub;
+  rhc.rh = NULL;
+  rhc.rh_tail = NULL;
+  rhc.pg = pg;
+  rhc.status = GNUNET_OK;
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_set_zero (pg->currency,
+                                        &rhc.balance_in));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_set_zero (pg->currency,
+                                        &rhc.balance_out));
+  qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */
+  for (unsigned int i = 0; NULL != work[i].cb; i++)
+  {
+    qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                               work[i].statement,
+                                               params,
+                                               work[i].cb,
+                                               &rhc);
+    if ( (0 > qs) ||
+         (GNUNET_OK != rhc.status) )
+      break;
+  }
+  if ( (qs < 0) ||
+       (rhc.status != GNUNET_OK) )
+  {
+    TEH_COMMON_free_reserve_history (cls,
+                                     rhc.rh);
+    rhc.rh = NULL;
+    if (qs >= 0)
+    {
+      /* status == SYSERR is a very hard error... */
+      qs = GNUNET_DB_STATUS_HARD_ERROR;
+    }
+  }
+  *rhp = rhc.rh;
+  GNUNET_assert (0 <=
+                 TALER_amount_subtract (balance,
+                                        &rhc.balance_in,
+                                        &rhc.balance_out));
+  return qs;
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_reserve_status (void *cls,
+                           const struct TALER_ReservePublicKeyP *reserve_pub,
+                           struct TALER_Amount *balance_in,
+                           struct TALER_Amount *balance_out,
+                           struct TALER_EXCHANGEDB_ReserveHistory **rhp)
+{
+  struct PostgresClosure *pg = cls;
+  struct ReserveHistoryContext rhc;
+  struct
+  {
+    /**
+     * Name of the prepared statement to run.
+     */
+    const char *statement;
+    /**
+     * Function to use to process the results.
+     */
+    GNUNET_PQ_PostgresResultHandler cb;
+  } work[] = {
+    /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */
+    { "reserves_in_get_transactions_truncated",
+      add_bank_to_exchange },
+    /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */
+    { "get_reserves_out_truncated",
+      &add_withdraw_coin },
+    /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */
+    { "recoup_by_reserve_truncated",
+      &add_recoup },
+    /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */
+    { "close_by_reserve_truncated",
+      &add_exchange_to_bank },
+    /** #TALER_EXCHANGEDB_RO_PURSE_MERGE */
+    { "merge_by_reserve_truncated",
+      &add_p2p_merge },
+    /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */
+    { "history_by_reserve_truncated",
+      &add_history_requests },
+    /* List terminator */
+    { NULL,
+      NULL }
+  };
+  enum GNUNET_DB_QueryStatus qs;
+  struct GNUNET_TIME_Absolute timelimit;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+    GNUNET_PQ_query_param_absolute_time (&timelimit),
+    GNUNET_PQ_query_param_end
+  };
+
+  PREPARE (pg,
+           "reserves_in_get_transactions_truncated",
+           /*
+           "SELECT"
+           " wire_reference"
+           ",credit_val"
+           ",credit_frac"
+           ",execution_date"
+           ",payto_uri AS sender_account_details"
+           " FROM reserves_in"
+           " JOIN wire_targets"
+           "   ON (wire_source_h_payto = wire_target_h_payto)"
+           " WHERE reserve_pub=$1"
+           "   AND execution_date>=$2;",
+           */
+           "WITH ri AS MATERIALIZED ( "
+           "  SELECT * "
+           "  FROM reserves_in "
+           "  WHERE reserve_pub = $1 "
+           ") "
+           "SELECT  "
+           "  wire_reference "
+           "  ,credit_val "
+           "  ,credit_frac "
+           "  ,execution_date "
+           "  ,payto_uri AS sender_account_details "
+           "FROM wire_targets "
+           "JOIN ri  "
+           "  ON (wire_target_h_payto = wire_source_h_payto) "
+           "WHERE execution_date >= $2"
+           "  AND wire_target_h_payto = ( "
+           "  SELECT wire_source_h_payto FROM ri "
+           "); ");
+  PREPARE (pg,
+           "get_reserves_out_truncated",
+           /*
+           "SELECT"
+           " ro.h_blind_ev"
+           ",denom.denom_pub_hash"
+           ",ro.denom_sig"
+           ",ro.reserve_sig"
+           ",ro.execution_date"
+           ",ro.amount_with_fee_val"
+           ",ro.amount_with_fee_frac"
+           ",denom.fee_withdraw_val"
+           ",denom.fee_withdraw_frac"
+           " FROM reserves res"
+           " JOIN reserves_out_by_reserve ror"
+           "   ON (res.reserve_uuid = ror.reserve_uuid)"
+           " JOIN reserves_out ro"
+           "   ON (ro.h_blind_ev = ror.h_blind_ev)"
+           " JOIN denominations denom"
+           "   ON (ro.denominations_serial = denom.denominations_serial)"
+           " WHERE res.reserve_pub=$1"
+           "   AND execution_date>=$2;",
+           */
+           "WITH robr AS MATERIALIZED ( "
+           "  SELECT h_blind_ev "
+           "  FROM reserves_out_by_reserve "
+           "  WHERE reserve_uuid= ( "
+           "    SELECT reserve_uuid "
+           "    FROM reserves "
+           "    WHERE reserve_pub = $1 "
+           "  ) "
+           ") SELECT "
+           "  ro.h_blind_ev "
+           "  ,denom.denom_pub_hash "
+           "  ,ro.denom_sig "
+           "  ,ro.reserve_sig "
+           "  ,ro.execution_date "
+           "  ,ro.amount_with_fee_val "
+           "  ,ro.amount_with_fee_frac "
+           "  ,denom.fee_withdraw_val "
+           "  ,denom.fee_withdraw_frac "
+           "FROM robr "
+           "JOIN reserves_out ro "
+           "  ON (ro.h_blind_ev = robr.h_blind_ev) "
+           "JOIN denominations denom "
+           "  ON (ro.denominations_serial = denom.denominations_serial)"
+           " WHERE ro.execution_date>=$2;");
+
+  PREPARE (pg,
+           "recoup_by_reserve_truncated",
+           /*
+           "SELECT"
+           " recoup.coin_pub"
+           ",recoup.coin_sig"
+           ",recoup.coin_blind"
+           ",recoup.amount_val"
+           ",recoup.amount_frac"
+           ",recoup.recoup_timestamp"
+           ",denominations.denom_pub_hash"
+           ",known_coins.denom_sig"
+           " FROM denominations"
+           " JOIN (known_coins"
+           "   JOIN recoup "
+           "   ON (recoup.coin_pub = known_coins.coin_pub))"
+           "  ON (known_coins.denominations_serial = 
denominations.denominations_serial)"
+           " WHERE recoup_timestamp>=$2"
+           " AND recoup.coin_pub"
+           "  IN (SELECT coin_pub"
+           "     FROM recoup_by_reserve"
+           "     JOIN (reserves_out"
+           "       JOIN (reserves_out_by_reserve"
+           "         JOIN reserves"
+           "           ON (reserves.reserve_uuid = 
reserves_out_by_reserve.reserve_uuid))"
+           "       ON (reserves_out_by_reserve.h_blind_ev = 
reserves_out.h_blind_ev))"
+           "     ON (recoup_by_reserve.reserve_out_serial_id = 
reserves_out.reserve_out_serial_id)"
+           "     WHERE reserves.reserve_pub=$1);",
+           */
+           "SELECT robr.coin_pub "
+           "  ,robr.coin_sig "
+           "  ,robr.coin_blind "
+           "  ,robr.amount_val "
+           "  ,robr.amount_frac "
+           "  ,robr.recoup_timestamp "
+           "  ,denominations.denom_pub_hash "
+           "  ,robr.denom_sig "
+           "FROM denominations "
+           "  JOIN exchange_do_recoup_by_reserve($1) robr"
+           "    USING (denominations_serial)"
+           " WHERE recoup_timestamp>=$2;");
+  /* Used in #postgres_get_reserve_status() */
+  PREPARE (pg,
+           "close_by_reserve_truncated",
+           "SELECT"
+           " amount_val"
+           ",amount_frac"
+           ",closing_fee_val"
+           ",closing_fee_frac"
+           ",execution_date"
+           ",payto_uri AS receiver_account"
+           ",wtid"
+           " FROM reserves_close"
+           "   JOIN wire_targets"
+           "     USING (wire_target_h_payto)"
+           " WHERE reserve_pub=$1"
+           "   AND execution_date>=$2;");
+
+  /* Used in #postgres_get_reserve_status() */
+  PREPARE (pg,
+           "merge_by_reserve_truncated",
+           "SELECT"
+           " pr.amount_with_fee_val"
+           ",pr.amount_with_fee_frac"
+           ",pr.balance_val"
+           ",pr.balance_frac"
+           ",pr.purse_fee_val"
+           ",pr.purse_fee_frac"
+           ",pr.h_contract_terms"
+           ",pr.merge_pub"
+           ",am.reserve_sig"
+           ",pm.purse_pub"
+           ",pm.merge_timestamp"
+           ",pr.purse_expiration"
+           ",pr.age_limit"
+           ",pr.flags"
+           " FROM purse_merges pm"
+           "   JOIN purse_requests pr"
+           "     USING (purse_pub)"
+           "   JOIN account_merges am"
+           "     ON (am.purse_pub = pm.purse_pub AND"
+           "         am.reserve_pub = pm.reserve_pub)"
+           " WHERE pm.reserve_pub=$1"
+           "  AND pm.merge_timestamp >= $2"
+           "  AND pm.partner_serial_id=0" /* must be local! */
+           "  AND pr.finished"
+           "  AND NOT pr.refunded;");
+
+  /* Used in #postgres_get_reserve_status() */
+  PREPARE (pg,
+           "history_by_reserve_truncated",
+           "SELECT"
+           " history_fee_val"
+           ",history_fee_frac"
+           ",request_timestamp"
+           ",reserve_sig"
+           " FROM history_requests"
+           " WHERE reserve_pub=$1"
+           "  AND request_timestamp>=$2;");
+
+  timelimit = GNUNET_TIME_absolute_subtract (
+    GNUNET_TIME_absolute_get (),
+    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS,
+                                   5));
+  rhc.reserve_pub = reserve_pub;
+  rhc.rh = NULL;
+  rhc.rh_tail = NULL;
+  rhc.pg = pg;
+  rhc.status = GNUNET_OK;
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_set_zero (pg->currency,
+                                        &rhc.balance_in));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_set_zero (pg->currency,
+                                        &rhc.balance_out));
+  qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */
+  for (unsigned int i = 0; NULL != work[i].cb; i++)
+  {
+    qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                               work[i].statement,
+                                               params,
+                                               work[i].cb,
+                                               &rhc);
+    if ( (0 > qs) ||
+         (GNUNET_OK != rhc.status) )
+      break;
+  }
+  if ( (qs < 0) ||
+       (rhc.status != GNUNET_OK) )
+  {
+    TEH_COMMON_free_reserve_history (cls,
+                                     rhc.rh);
+    rhc.rh = NULL;
+    if (qs >= 0)
+    {
+      /* status == SYSERR is a very hard error... */
+      qs = GNUNET_DB_STATUS_HARD_ERROR;
+    }
+  }
+  *rhp = rhc.rh;
+  *balance_in = rhc.balance_in;
+  *balance_out = rhc.balance_out;
+  return qs;
+}
diff --git a/src/exchangedb/pg_get_reserve_history.h 
b/src/exchangedb/pg_get_reserve_history.h
new file mode 100644
index 00000000..ee47996b
--- /dev/null
+++ b/src/exchangedb/pg_get_reserve_history.h
@@ -0,0 +1,67 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 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 pg_get_reserve_history.h
+ * @brief implementation of the get_reserve_history function
+ * @author Christian Grothoff
+ */
+#ifndef PG_GET_RESERVE_HISTORY_H
+#define PG_GET_RESERVE_HISTORY_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Get all of the transaction history associated with the specified
+ * reserve.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param reserve_pub public key of the reserve
+ * @param[out] balance set to the reserve balance
+ * @param[out] rhp set to known transaction history (NULL if reserve is 
unknown)
+ * @return transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_reserve_history (void *cls,
+                            const struct TALER_ReservePublicKeyP *reserve_pub,
+                            struct TALER_Amount *balance,
+                            struct TALER_EXCHANGEDB_ReserveHistory **rhp);
+
+
+/**
+ * Get a truncated transaction history associated with the specified
+ * reserve.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param reserve_pub public key of the reserve
+ * @param[out] balance_in set to the total of inbound
+ *             transactions in the returned history
+ * @param[out] balance_out set to the total of outbound
+ *             transactions in the returned history
+ * @param[out] rhp set to known transaction history (NULL if reserve is 
unknown)
+ * @return transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_reserve_status (void *cls,
+                           const struct TALER_ReservePublicKeyP *reserve_pub,
+                           struct TALER_Amount *balance_in,
+                           struct TALER_Amount *balance_out,
+                           struct TALER_EXCHANGEDB_ReserveHistory **rhp);
+
+
+#endif
diff --git a/src/exchangedb/plugin_exchangedb_common.c 
b/src/exchangedb/plugin_exchangedb_common.c
index a0159fb8..562710ea 100644
--- a/src/exchangedb/plugin_exchangedb_common.c
+++ b/src/exchangedb/plugin_exchangedb_common.c
@@ -85,6 +85,22 @@ TEH_COMMON_free_reserve_history (
         GNUNET_free (history);
         break;
       }
+    case TALER_EXCHANGEDB_RO_OPEN_REQUEST:
+      {
+        struct TALER_EXCHANGEDB_OpenRequest *or;
+
+        or = rh->details.open_request;
+        GNUNET_free (or);
+        break;
+      }
+    case TALER_EXCHANGEDB_RO_CLOSE_REQUEST:
+      {
+        struct TALER_EXCHANGEDB_CloseRequest *cr;
+
+        cr = rh->details.close_request;
+        GNUNET_free (cr);
+        break;
+      }
     }
     {
       struct TALER_EXCHANGEDB_ReserveHistory *next;
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 940523b7..af441d95 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -34,6 +34,7 @@
 #include "pg_do_reserve_open.h"
 #include "pg_get_coin_transactions.h"
 #include "pg_get_expired_reserves.h"
+#include "pg_get_reserve_history.h"
 #include "pg_get_unfinished_close_requests.h"
 #include "pg_insert_close_request.h"
 #include "pg_insert_records_by_table.h"
@@ -684,72 +685,8 @@ prepare_statements (struct PostgresClosure *pg)
       " ORDER BY reserve_in_serial_id;"),
     /* Used in #postgres_get_reserve_history() to obtain inbound transactions
        for a reserve */
-    GNUNET_PQ_make_prepare (
-      "reserves_in_get_transactions",
-      /*
-      "SELECT"
-      " wire_reference"
-      ",credit_val"
-      ",credit_frac"
-      ",execution_date"
-      ",payto_uri AS sender_account_details"
-      " FROM reserves_in"
-      " JOIN wire_targets"
-      "   ON (wire_source_h_payto = wire_target_h_payto)"
-      " WHERE reserve_pub=$1;",
-      */
-      "WITH ri AS MATERIALIZED ( "
-      "  SELECT * "
-      "  FROM reserves_in "
-      "  WHERE reserve_pub = $1 "
-      ") "
-      "SELECT  "
-      "  wire_reference "
-      "  ,credit_val "
-      "  ,credit_frac "
-      "  ,execution_date "
-      "  ,payto_uri AS sender_account_details "
-      "FROM wire_targets "
-      "JOIN ri  "
-      "  ON (wire_target_h_payto = wire_source_h_payto) "
-      "WHERE wire_target_h_payto = ( "
-      "  SELECT wire_source_h_payto FROM ri "
-      "); "),
     /* Used in #postgres_get_reserve_status() to obtain inbound transactions
        for a reserve */
-    GNUNET_PQ_make_prepare (
-      "reserves_in_get_transactions_truncated",
-      /*
-      "SELECT"
-      " wire_reference"
-      ",credit_val"
-      ",credit_frac"
-      ",execution_date"
-      ",payto_uri AS sender_account_details"
-      " FROM reserves_in"
-      " JOIN wire_targets"
-      "   ON (wire_source_h_payto = wire_target_h_payto)"
-      " WHERE reserve_pub=$1"
-      "   AND execution_date>=$2;",
-      */
-      "WITH ri AS MATERIALIZED ( "
-      "  SELECT * "
-      "  FROM reserves_in "
-      "  WHERE reserve_pub = $1 "
-      ") "
-      "SELECT  "
-      "  wire_reference "
-      "  ,credit_val "
-      "  ,credit_frac "
-      "  ,execution_date "
-      "  ,payto_uri AS sender_account_details "
-      "FROM wire_targets "
-      "JOIN ri  "
-      "  ON (wire_target_h_payto = wire_source_h_payto) "
-      "WHERE execution_date >= $2"
-      "  AND wire_target_h_payto = ( "
-      "  SELECT wire_source_h_payto FROM ri "
-      "); "),
     /* Used in #postgres_do_withdraw() to store
        the signature of a blinded coin with the blinded coin's
        details before returning it during /reserve/withdraw. We store
@@ -879,108 +816,7 @@ prepare_statements (struct PostgresClosure *pg)
       "    JOIN denominations denom"
       "      USING (denominations_serial)"
       " WHERE h_blind_ev=$1;"),
-    /* Used during #postgres_get_reserve_history() to
-       obtain all of the /reserve/withdraw operations that
-       have been performed on a given reserve. (i.e. to
-       demonstrate double-spending) */
-    GNUNET_PQ_make_prepare (
-      "get_reserves_out",
-      /*
-      "SELECT"
-      " ro.h_blind_ev"
-      ",denom.denom_pub_hash"
-      ",ro.denom_sig"
-      ",ro.reserve_sig"
-      ",ro.execution_date"
-      ",ro.amount_with_fee_val"
-      ",ro.amount_with_fee_frac"
-      ",denom.fee_withdraw_val"
-      ",denom.fee_withdraw_frac"
-      " FROM reserves res"
-      " JOIN reserves_out_by_reserve ror"
-      "   ON (res.reserve_uuid = ror.reserve_uuid)"
-      " JOIN reserves_out ro"
-      "   ON (ro.h_blind_ev = ror.h_blind_ev)"
-      " JOIN denominations denom"
-      "   ON (ro.denominations_serial = denom.denominations_serial)"
-      " WHERE res.reserve_pub=$1;",
-      */
-      "WITH robr AS MATERIALIZED ( "
-      "  SELECT h_blind_ev "
-      "  FROM reserves_out_by_reserve "
-      "  WHERE reserve_uuid= ( "
-      "    SELECT reserve_uuid "
-      "    FROM reserves "
-      "    WHERE reserve_pub = $1 "
-      "  ) "
-      ") SELECT "
-      "  ro.h_blind_ev "
-      "  ,denom.denom_pub_hash "
-      "  ,ro.denom_sig "
-      "  ,ro.reserve_sig "
-      "  ,ro.execution_date "
-      "  ,ro.amount_with_fee_val "
-      "  ,ro.amount_with_fee_frac "
-      "  ,denom.fee_withdraw_val "
-      "  ,denom.fee_withdraw_frac "
-      "FROM robr "
-      "JOIN reserves_out ro "
-      "  ON (ro.h_blind_ev = robr.h_blind_ev) "
-      "JOIN denominations denom "
-      "  ON (ro.denominations_serial = denom.denominations_serial);"),
-    /* Used during #postgres_get_reserve_status() to
-       obtain all of the /reserve/withdraw operations that
-       have been performed on a given reserve. (i.e. to
-       demonstrate double-spending) */
-    GNUNET_PQ_make_prepare (
-      "get_reserves_out_truncated",
-      /*
-      "SELECT"
-      " ro.h_blind_ev"
-      ",denom.denom_pub_hash"
-      ",ro.denom_sig"
-      ",ro.reserve_sig"
-      ",ro.execution_date"
-      ",ro.amount_with_fee_val"
-      ",ro.amount_with_fee_frac"
-      ",denom.fee_withdraw_val"
-      ",denom.fee_withdraw_frac"
-      " FROM reserves res"
-      " JOIN reserves_out_by_reserve ror"
-      "   ON (res.reserve_uuid = ror.reserve_uuid)"
-      " JOIN reserves_out ro"
-      "   ON (ro.h_blind_ev = ror.h_blind_ev)"
-      " JOIN denominations denom"
-      "   ON (ro.denominations_serial = denom.denominations_serial)"
-      " WHERE res.reserve_pub=$1"
-      "   AND execution_date>=$2;",
-      */
-      "WITH robr AS MATERIALIZED ( "
-      "  SELECT h_blind_ev "
-      "  FROM reserves_out_by_reserve "
-      "  WHERE reserve_uuid= ( "
-      "    SELECT reserve_uuid "
-      "    FROM reserves "
-      "    WHERE reserve_pub = $1 "
-      "  ) "
-      ") SELECT "
-      "  ro.h_blind_ev "
-      "  ,denom.denom_pub_hash "
-      "  ,ro.denom_sig "
-      "  ,ro.reserve_sig "
-      "  ,ro.execution_date "
-      "  ,ro.amount_with_fee_val "
-      "  ,ro.amount_with_fee_frac "
-      "  ,denom.fee_withdraw_val "
-      "  ,denom.fee_withdraw_frac "
-      "FROM robr "
-      "JOIN reserves_out ro "
-      "  ON (ro.h_blind_ev = robr.h_blind_ev) "
-      "JOIN denominations denom "
-      "  ON (ro.denominations_serial = denom.denominations_serial)"
-      " WHERE ro.execution_date>=$2;"),
     /* Used in #postgres_select_withdrawals_above_serial_id() */
-
     GNUNET_PQ_make_prepare (
       "get_reserve_balance",
       "SELECT"
@@ -2030,203 +1866,6 @@ prepare_statements (struct PostgresClosure *pg)
       "     USING (reserve_pub)"
       " WHERE close_uuid>=$1"
       " ORDER BY close_uuid ASC;"),
-    /* Used in #postgres_get_reserve_history() to obtain recoup transactions
-       for a reserve - query optimization should be disabled i.e.
-       BEGIN; SET LOCAL join_collapse_limit=1; query; COMMIT; */
-    GNUNET_PQ_make_prepare (
-      "recoup_by_reserve",
-      /*
-      "SELECT"
-      " recoup.coin_pub"
-      ",recoup.coin_sig"
-      ",recoup.coin_blind"
-      ",recoup.amount_val"
-      ",recoup.amount_frac"
-      ",recoup.recoup_timestamp"
-      ",denominations.denom_pub_hash"
-      ",known_coins.denom_sig"
-      " FROM denominations"
-      " JOIN (known_coins"
-      "   JOIN recoup "
-      "   ON (recoup.coin_pub = known_coins.coin_pub))"
-      "  ON (known_coins.denominations_serial = 
denominations.denominations_serial)"
-      " WHERE recoup.coin_pub"
-      " IN (SELECT coin_pub"
-      "     FROM recoup_by_reserve"
-      "     JOIN (reserves_out"
-      "       JOIN (reserves_out_by_reserve"
-      "         JOIN reserves"
-      "           ON (reserves.reserve_uuid = 
reserves_out_by_reserve.reserve_uuid))"
-      "       ON (reserves_out_by_reserve.h_blind_ev = 
reserves_out.h_blind_ev))"
-      "     ON (recoup_by_reserve.reserve_out_serial_id = 
reserves_out.reserve_out_serial_id)"
-      "     WHERE reserves.reserve_pub=$1);",
-      */
-      "SELECT robr.coin_pub "
-      "  ,robr.coin_sig "
-      "  ,robr.coin_blind "
-      "  ,robr.amount_val "
-      "  ,robr.amount_frac "
-      "  ,robr.recoup_timestamp "
-      "  ,denominations.denom_pub_hash "
-      "  ,robr.denom_sig "
-      "FROM denominations "
-      "  JOIN exchange_do_recoup_by_reserve($1) robr"
-      " USING (denominations_serial);"),
-    /* Used in #postgres_get_reserve_status() to obtain recoup transactions
-       for a reserve - query optimization should be disabled i.e.
-       BEGIN; SET LOCAL join_collapse_limit=1; query; COMMIT; */
-    GNUNET_PQ_make_prepare (
-      "recoup_by_reserve_truncated",
-      /*
-      "SELECT"
-      " recoup.coin_pub"
-      ",recoup.coin_sig"
-      ",recoup.coin_blind"
-      ",recoup.amount_val"
-      ",recoup.amount_frac"
-      ",recoup.recoup_timestamp"
-      ",denominations.denom_pub_hash"
-      ",known_coins.denom_sig"
-      " FROM denominations"
-      " JOIN (known_coins"
-      "   JOIN recoup "
-      "   ON (recoup.coin_pub = known_coins.coin_pub))"
-      "  ON (known_coins.denominations_serial = 
denominations.denominations_serial)"
-      " WHERE recoup_timestamp>=$2"
-      " AND recoup.coin_pub"
-      "  IN (SELECT coin_pub"
-      "     FROM recoup_by_reserve"
-      "     JOIN (reserves_out"
-      "       JOIN (reserves_out_by_reserve"
-      "         JOIN reserves"
-      "           ON (reserves.reserve_uuid = 
reserves_out_by_reserve.reserve_uuid))"
-      "       ON (reserves_out_by_reserve.h_blind_ev = 
reserves_out.h_blind_ev))"
-      "     ON (recoup_by_reserve.reserve_out_serial_id = 
reserves_out.reserve_out_serial_id)"
-      "     WHERE reserves.reserve_pub=$1);",
-      */
-      "SELECT robr.coin_pub "
-      "  ,robr.coin_sig "
-      "  ,robr.coin_blind "
-      "  ,robr.amount_val "
-      "  ,robr.amount_frac "
-      "  ,robr.recoup_timestamp "
-      "  ,denominations.denom_pub_hash "
-      "  ,robr.denom_sig "
-      "FROM denominations "
-      "  JOIN exchange_do_recoup_by_reserve($1) robr"
-      "    USING (denominations_serial)"
-      " WHERE recoup_timestamp>=$2;"),
-    /* Used in #postgres_get_reserve_history() */
-    GNUNET_PQ_make_prepare (
-      "close_by_reserve",
-      "SELECT"
-      " amount_val"
-      ",amount_frac"
-      ",closing_fee_val"
-      ",closing_fee_frac"
-      ",execution_date"
-      ",payto_uri AS receiver_account"
-      ",wtid"
-      " FROM reserves_close"
-      "   JOIN wire_targets"
-      "     USING (wire_target_h_payto)"
-      " WHERE reserve_pub=$1;"),
-    /* Used in #postgres_get_reserve_status() */
-    GNUNET_PQ_make_prepare (
-      "close_by_reserve_truncated",
-      "SELECT"
-      " amount_val"
-      ",amount_frac"
-      ",closing_fee_val"
-      ",closing_fee_frac"
-      ",execution_date"
-      ",payto_uri AS receiver_account"
-      ",wtid"
-      " FROM reserves_close"
-      "   JOIN wire_targets"
-      "     USING (wire_target_h_payto)"
-      " WHERE reserve_pub=$1"
-      "   AND execution_date>=$2;"),
-    /* Used in #postgres_get_reserve_history() */
-    GNUNET_PQ_make_prepare (
-      "merge_by_reserve",
-      "SELECT"
-      " pr.amount_with_fee_val"
-      ",pr.amount_with_fee_frac"
-      ",pr.balance_val"
-      ",pr.balance_frac"
-      ",pr.purse_fee_val"
-      ",pr.purse_fee_frac"
-      ",pr.h_contract_terms"
-      ",pr.merge_pub"
-      ",am.reserve_sig"
-      ",pm.purse_pub"
-      ",pm.merge_timestamp"
-      ",pr.purse_expiration"
-      ",pr.age_limit"
-      ",pr.flags"
-      " FROM purse_merges pm"
-      "   JOIN purse_requests pr"
-      "     USING (purse_pub)"
-      "   JOIN account_merges am"
-      "     ON (am.purse_pub = pm.purse_pub AND"
-      "         am.reserve_pub = pm.reserve_pub)"
-      " WHERE pm.reserve_pub=$1"
-      "  AND pm.partner_serial_id=0" /* must be local! */
-      "  AND pr.finished"
-      "  AND NOT pr.refunded;"),
-    /* Used in #postgres_get_reserve_status() */
-    GNUNET_PQ_make_prepare (
-      "merge_by_reserve_truncated",
-      "SELECT"
-      " pr.amount_with_fee_val"
-      ",pr.amount_with_fee_frac"
-      ",pr.balance_val"
-      ",pr.balance_frac"
-      ",pr.purse_fee_val"
-      ",pr.purse_fee_frac"
-      ",pr.h_contract_terms"
-      ",pr.merge_pub"
-      ",am.reserve_sig"
-      ",pm.purse_pub"
-      ",pm.merge_timestamp"
-      ",pr.purse_expiration"
-      ",pr.age_limit"
-      ",pr.flags"
-      " FROM purse_merges pm"
-      "   JOIN purse_requests pr"
-      "     USING (purse_pub)"
-      "   JOIN account_merges am"
-      "     ON (am.purse_pub = pm.purse_pub AND"
-      "         am.reserve_pub = pm.reserve_pub)"
-      " WHERE pm.reserve_pub=$1"
-      "  AND pm.merge_timestamp >= $2"
-      "  AND pm.partner_serial_id=0" /* must be local! */
-      "  AND pr.finished"
-      "  AND NOT pr.refunded;"),
-    /* Used in #postgres_get_reserve_history() */
-    GNUNET_PQ_make_prepare (
-      "history_by_reserve",
-      "SELECT"
-      " history_fee_val"
-      ",history_fee_frac"
-      ",request_timestamp"
-      ",reserve_sig"
-      " FROM history_requests"
-      " WHERE reserve_pub=$1;"),
-    /* Used in #postgres_get_reserve_status() */
-    GNUNET_PQ_make_prepare (
-      "history_by_reserve_truncated",
-      "SELECT"
-      " history_fee_val"
-      ",history_fee_frac"
-      ",request_timestamp"
-      ",reserve_sig"
-      " FROM history_requests"
-      " WHERE reserve_pub=$1"
-      "  AND request_timestamp>=$2;"),
-    /* Used in #postgres_get_coin_transactions() to obtain recoup transactions
-       for a coin */
     /* Used in #postgres_get_reserve_by_h_blind() */
     GNUNET_PQ_make_prepare (
       "reserve_by_h_blind",
@@ -4871,670 +4510,6 @@ postgres_do_recoup_refresh (
 }
 
 
-/**
- * Closure for callbacks invoked via #postgres_get_reserve_history.
- */
-struct ReserveHistoryContext
-{
-
-  /**
-   * Which reserve are we building the history for?
-   */
-  const struct TALER_ReservePublicKeyP *reserve_pub;
-
-  /**
-   * Where we build the history.
-   */
-  struct TALER_EXCHANGEDB_ReserveHistory *rh;
-
-  /**
-   * Tail of @e rh list.
-   */
-  struct TALER_EXCHANGEDB_ReserveHistory *rh_tail;
-
-  /**
-   * Plugin context.
-   */
-  struct PostgresClosure *pg;
-
-  /**
-   * Sum of all credit transactions.
-   */
-  struct TALER_Amount balance_in;
-
-  /**
-   * Sum of all debit transactions.
-   */
-  struct TALER_Amount balance_out;
-
-  /**
-   * Set to #GNUNET_SYSERR on serious internal errors during
-   * the callbacks.
-   */
-  enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Append and return a fresh element to the reserve
- * history kept in @a rhc.
- *
- * @param rhc where the history is kept
- * @return the fresh element that was added
- */
-static struct TALER_EXCHANGEDB_ReserveHistory *
-append_rh (struct ReserveHistoryContext *rhc)
-{
-  struct TALER_EXCHANGEDB_ReserveHistory *tail;
-
-  tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory);
-  if (NULL != rhc->rh_tail)
-  {
-    rhc->rh_tail->next = tail;
-    rhc->rh_tail = tail;
-  }
-  else
-  {
-    rhc->rh_tail = tail;
-    rhc->rh = tail;
-  }
-  return tail;
-}
-
-
-/**
- * Add bank transfers to result set for #postgres_get_reserve_history.
- *
- * @param cls a `struct ReserveHistoryContext *`
- * @param result SQL result
- * @param num_results number of rows in @a result
- */
-static void
-add_bank_to_exchange (void *cls,
-                      PGresult *result,
-                      unsigned int num_results)
-{
-  struct ReserveHistoryContext *rhc = cls;
-  struct PostgresClosure *pg = rhc->pg;
-
-  while (0 < num_results)
-  {
-    struct TALER_EXCHANGEDB_BankTransfer *bt;
-    struct TALER_EXCHANGEDB_ReserveHistory *tail;
-
-    bt = GNUNET_new (struct TALER_EXCHANGEDB_BankTransfer);
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        GNUNET_PQ_result_spec_uint64 ("wire_reference",
-                                      &bt->wire_reference),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("credit",
-                                     &bt->amount),
-        GNUNET_PQ_result_spec_timestamp ("execution_date",
-                                         &bt->execution_date),
-        GNUNET_PQ_result_spec_string ("sender_account_details",
-                                      &bt->sender_account_details),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    --num_results))
-      {
-        GNUNET_break (0);
-        GNUNET_free (bt);
-        rhc->status = GNUNET_SYSERR;
-        return;
-      }
-    }
-    GNUNET_assert (0 <=
-                   TALER_amount_add (&rhc->balance_in,
-                                     &rhc->balance_in,
-                                     &bt->amount));
-    bt->reserve_pub = *rhc->reserve_pub;
-    tail = append_rh (rhc);
-    tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE;
-    tail->details.bank = bt;
-  } /* end of 'while (0 < rows)' */
-}
-
-
-/**
- * Add coin withdrawals to result set for #postgres_get_reserve_history.
- *
- * @param cls a `struct ReserveHistoryContext *`
- * @param result SQL result
- * @param num_results number of rows in @a result
- */
-static void
-add_withdraw_coin (void *cls,
-                   PGresult *result,
-                   unsigned int num_results)
-{
-  struct ReserveHistoryContext *rhc = cls;
-  struct PostgresClosure *pg = rhc->pg;
-
-  while (0 < num_results)
-  {
-    struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc;
-    struct TALER_EXCHANGEDB_ReserveHistory *tail;
-
-    cbc = GNUNET_new (struct TALER_EXCHANGEDB_CollectableBlindcoin);
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
-                                              &cbc->h_coin_envelope),
-        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
-                                              &cbc->denom_pub_hash),
-        TALER_PQ_result_spec_blinded_denom_sig ("denom_sig",
-                                                &cbc->sig),
-        GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
-                                              &cbc->reserve_sig),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
-                                     &cbc->amount_with_fee),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
-                                     &cbc->withdraw_fee),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    --num_results))
-      {
-        GNUNET_break (0);
-        GNUNET_free (cbc);
-        rhc->status = GNUNET_SYSERR;
-        return;
-      }
-    }
-    GNUNET_assert (0 <=
-                   TALER_amount_add (&rhc->balance_out,
-                                     &rhc->balance_out,
-                                     &cbc->amount_with_fee));
-    cbc->reserve_pub = *rhc->reserve_pub;
-    tail = append_rh (rhc);
-    tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN;
-    tail->details.withdraw = cbc;
-  }
-}
-
-
-/**
- * Add recoups to result set for #postgres_get_reserve_history.
- *
- * @param cls a `struct ReserveHistoryContext *`
- * @param result SQL result
- * @param num_results number of rows in @a result
- */
-static void
-add_recoup (void *cls,
-            PGresult *result,
-            unsigned int num_results)
-{
-  struct ReserveHistoryContext *rhc = cls;
-  struct PostgresClosure *pg = rhc->pg;
-
-  while (0 < num_results)
-  {
-    struct TALER_EXCHANGEDB_Recoup *recoup;
-    struct TALER_EXCHANGEDB_ReserveHistory *tail;
-
-    recoup = GNUNET_new (struct TALER_EXCHANGEDB_Recoup);
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
-                                     &recoup->value),
-        GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
-                                              &recoup->coin.coin_pub),
-        GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
-                                              &recoup->coin_blind),
-        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
-                                              &recoup->coin_sig),
-        GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
-                                         &recoup->timestamp),
-        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
-                                              &recoup->coin.denom_pub_hash),
-        TALER_PQ_result_spec_denom_sig (
-          "denom_sig",
-          &recoup->coin.denom_sig),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    --num_results))
-      {
-        GNUNET_break (0);
-        GNUNET_free (recoup);
-        rhc->status = GNUNET_SYSERR;
-        return;
-      }
-    }
-    GNUNET_assert (0 <=
-                   TALER_amount_add (&rhc->balance_in,
-                                     &rhc->balance_in,
-                                     &recoup->value));
-    recoup->reserve_pub = *rhc->reserve_pub;
-    tail = append_rh (rhc);
-    tail->type = TALER_EXCHANGEDB_RO_RECOUP_COIN;
-    tail->details.recoup = recoup;
-  } /* end of 'while (0 < rows)' */
-}
-
-
-/**
- * Add exchange-to-bank transfers to result set for
- * #postgres_get_reserve_history.
- *
- * @param cls a `struct ReserveHistoryContext *`
- * @param result SQL result
- * @param num_results number of rows in @a result
- */
-static void
-add_exchange_to_bank (void *cls,
-                      PGresult *result,
-                      unsigned int num_results)
-{
-  struct ReserveHistoryContext *rhc = cls;
-  struct PostgresClosure *pg = rhc->pg;
-
-  while (0 < num_results)
-  {
-    struct TALER_EXCHANGEDB_ClosingTransfer *closing;
-    struct TALER_EXCHANGEDB_ReserveHistory *tail;
-
-    closing = GNUNET_new (struct TALER_EXCHANGEDB_ClosingTransfer);
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
-                                     &closing->amount),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
-                                     &closing->closing_fee),
-        GNUNET_PQ_result_spec_timestamp ("execution_date",
-                                         &closing->execution_date),
-        GNUNET_PQ_result_spec_string ("receiver_account",
-                                      &closing->receiver_account_details),
-        GNUNET_PQ_result_spec_auto_from_type ("wtid",
-                                              &closing->wtid),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    --num_results))
-      {
-        GNUNET_break (0);
-        GNUNET_free (closing);
-        rhc->status = GNUNET_SYSERR;
-        return;
-      }
-    }
-    GNUNET_assert (0 <=
-                   TALER_amount_add (&rhc->balance_out,
-                                     &rhc->balance_out,
-                                     &closing->amount));
-    closing->reserve_pub = *rhc->reserve_pub;
-    tail = append_rh (rhc);
-    tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK;
-    tail->details.closing = closing;
-  } /* end of 'while (0 < rows)' */
-}
-
-
-/**
- * Add purse merge transfers to result set for
- * #postgres_get_reserve_history.
- *
- * @param cls a `struct ReserveHistoryContext *`
- * @param result SQL result
- * @param num_results number of rows in @a result
- */
-static void
-add_p2p_merge (void *cls,
-               PGresult *result,
-               unsigned int num_results)
-{
-  struct ReserveHistoryContext *rhc = cls;
-  struct PostgresClosure *pg = rhc->pg;
-
-  while (0 < num_results)
-  {
-    struct TALER_EXCHANGEDB_PurseMerge *merge;
-    struct TALER_EXCHANGEDB_ReserveHistory *tail;
-
-    merge = GNUNET_new (struct TALER_EXCHANGEDB_PurseMerge);
-    {
-      uint32_t flags32;
-      struct TALER_Amount balance;
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee",
-                                     &merge->purse_fee),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
-                                     &balance),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
-                                     &merge->amount_with_fee),
-        GNUNET_PQ_result_spec_timestamp ("merge_timestamp",
-                                         &merge->merge_timestamp),
-        GNUNET_PQ_result_spec_timestamp ("purse_expiration",
-                                         &merge->purse_expiration),
-        GNUNET_PQ_result_spec_uint32 ("age_limit",
-                                      &merge->min_age),
-        GNUNET_PQ_result_spec_uint32 ("flags",
-                                      &flags32),
-        GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
-                                              &merge->h_contract_terms),
-        GNUNET_PQ_result_spec_auto_from_type ("merge_pub",
-                                              &merge->merge_pub),
-        GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
-                                              &merge->purse_pub),
-        GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
-                                              &merge->reserve_sig),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    --num_results))
-      {
-        GNUNET_break (0);
-        GNUNET_free (merge);
-        rhc->status = GNUNET_SYSERR;
-        return;
-      }
-      merge->flags = (enum TALER_WalletAccountMergeFlags) flags32;
-      if ( (! GNUNET_TIME_absolute_is_future (
-              merge->merge_timestamp.abs_time)) &&
-           (-1 != TALER_amount_cmp (&balance,
-                                    &merge->amount_with_fee)) )
-        merge->merged = true;
-    }
-    if (merge->merged)
-      GNUNET_assert (0 <=
-                     TALER_amount_add (&rhc->balance_in,
-                                       &rhc->balance_in,
-                                       &merge->amount_with_fee));
-    GNUNET_assert (0 <=
-                   TALER_amount_add (&rhc->balance_out,
-                                     &rhc->balance_out,
-                                     &merge->purse_fee));
-    merge->reserve_pub = *rhc->reserve_pub;
-    tail = append_rh (rhc);
-    tail->type = TALER_EXCHANGEDB_RO_PURSE_MERGE;
-    tail->details.merge = merge;
-  }
-}
-
-
-/**
- * Add paid for history requests to result set for
- * #postgres_get_reserve_history.
- *
- * @param cls a `struct ReserveHistoryContext *`
- * @param result SQL result
- * @param num_results number of rows in @a result
- */
-static void
-add_history_requests (void *cls,
-                      PGresult *result,
-                      unsigned int num_results)
-{
-  struct ReserveHistoryContext *rhc = cls;
-  struct PostgresClosure *pg = rhc->pg;
-
-  while (0 < num_results)
-  {
-    struct TALER_EXCHANGEDB_HistoryRequest *history;
-    struct TALER_EXCHANGEDB_ReserveHistory *tail;
-
-    history = GNUNET_new (struct TALER_EXCHANGEDB_HistoryRequest);
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
-                                     &history->history_fee),
-        GNUNET_PQ_result_spec_timestamp ("request_timestamp",
-                                         &history->request_timestamp),
-        GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
-                                              &history->reserve_sig),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    --num_results))
-      {
-        GNUNET_break (0);
-        GNUNET_free (history);
-        rhc->status = GNUNET_SYSERR;
-        return;
-      }
-    }
-    GNUNET_assert (0 <=
-                   TALER_amount_add (&rhc->balance_out,
-                                     &rhc->balance_out,
-                                     &history->history_fee));
-    history->reserve_pub = *rhc->reserve_pub;
-    tail = append_rh (rhc);
-    tail->type = TALER_EXCHANGEDB_RO_HISTORY_REQUEST;
-    tail->details.history = history;
-  }
-}
-
-
-/**
- * Get all of the transaction history associated with the specified
- * reserve.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param reserve_pub public key of the reserve
- * @param[out] balance set to the reserve balance
- * @param[out] rhp set to known transaction history (NULL if reserve is 
unknown)
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_reserve_history (void *cls,
-                              const struct TALER_ReservePublicKeyP 
*reserve_pub,
-                              struct TALER_Amount *balance,
-                              struct TALER_EXCHANGEDB_ReserveHistory **rhp)
-{
-  struct PostgresClosure *pg = cls;
-  struct ReserveHistoryContext rhc;
-  struct
-  {
-    /**
-     * Name of the prepared statement to run.
-     */
-    const char *statement;
-    /**
-     * Function to use to process the results.
-     */
-    GNUNET_PQ_PostgresResultHandler cb;
-  } work[] = {
-    /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */
-    { "reserves_in_get_transactions",
-      add_bank_to_exchange },
-    /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */
-    { "get_reserves_out",
-      &add_withdraw_coin },
-    /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */
-    { "recoup_by_reserve",
-      &add_recoup },
-    /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */
-    { "close_by_reserve",
-      &add_exchange_to_bank },
-    /** #TALER_EXCHANGEDB_RO_PURSE_MERGE */
-    { "merge_by_reserve",
-      &add_p2p_merge },
-    /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */
-    { "history_by_reserve",
-      &add_history_requests },
-    /* List terminator */
-    { NULL,
-      NULL }
-  };
-  enum GNUNET_DB_QueryStatus qs;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (reserve_pub),
-    GNUNET_PQ_query_param_end
-  };
-
-  rhc.reserve_pub = reserve_pub;
-  rhc.rh = NULL;
-  rhc.rh_tail = NULL;
-  rhc.pg = pg;
-  rhc.status = GNUNET_OK;
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_set_zero (pg->currency,
-                                        &rhc.balance_in));
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_set_zero (pg->currency,
-                                        &rhc.balance_out));
-  qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */
-  for (unsigned int i = 0; NULL != work[i].cb; i++)
-  {
-    qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
-                                               work[i].statement,
-                                               params,
-                                               work[i].cb,
-                                               &rhc);
-    if ( (0 > qs) ||
-         (GNUNET_OK != rhc.status) )
-      break;
-  }
-  if ( (qs < 0) ||
-       (rhc.status != GNUNET_OK) )
-  {
-    TEH_COMMON_free_reserve_history (cls,
-                                     rhc.rh);
-    rhc.rh = NULL;
-    if (qs >= 0)
-    {
-      /* status == SYSERR is a very hard error... */
-      qs = GNUNET_DB_STATUS_HARD_ERROR;
-    }
-  }
-  *rhp = rhc.rh;
-  GNUNET_assert (0 <=
-                 TALER_amount_subtract (balance,
-                                        &rhc.balance_in,
-                                        &rhc.balance_out));
-  return qs;
-}
-
-
-/**
- * Get a truncated transaction history associated with the specified
- * reserve.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param reserve_pub public key of the reserve
- * @param[out] balance_in set to the total of inbound
- *             transactions in the returned history
- * @param[out] balance_out set to the total of outbound
- *             transactions in the returned history
- * @param[out] rhp set to known transaction history (NULL if reserve is 
unknown)
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_reserve_status (void *cls,
-                             const struct TALER_ReservePublicKeyP *reserve_pub,
-                             struct TALER_Amount *balance_in,
-                             struct TALER_Amount *balance_out,
-                             struct TALER_EXCHANGEDB_ReserveHistory **rhp)
-{
-  struct PostgresClosure *pg = cls;
-  struct ReserveHistoryContext rhc;
-  struct
-  {
-    /**
-     * Name of the prepared statement to run.
-     */
-    const char *statement;
-    /**
-     * Function to use to process the results.
-     */
-    GNUNET_PQ_PostgresResultHandler cb;
-  } work[] = {
-    /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */
-    { "reserves_in_get_transactions_truncated",
-      add_bank_to_exchange },
-    /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */
-    { "get_reserves_out_truncated",
-      &add_withdraw_coin },
-    /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */
-    { "recoup_by_reserve_truncated",
-      &add_recoup },
-    /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */
-    { "close_by_reserve_truncated",
-      &add_exchange_to_bank },
-    /** #TALER_EXCHANGEDB_RO_PURSE_MERGE */
-    { "merge_by_reserve_truncated",
-      &add_p2p_merge },
-    /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */
-    { "history_by_reserve_truncated",
-      &add_history_requests },
-    /* List terminator */
-    { NULL,
-      NULL }
-  };
-  enum GNUNET_DB_QueryStatus qs;
-  struct GNUNET_TIME_Absolute timelimit;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (reserve_pub),
-    GNUNET_PQ_query_param_absolute_time (&timelimit),
-    GNUNET_PQ_query_param_end
-  };
-
-  timelimit = GNUNET_TIME_absolute_subtract (
-    GNUNET_TIME_absolute_get (),
-    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS,
-                                   5));
-  rhc.reserve_pub = reserve_pub;
-  rhc.rh = NULL;
-  rhc.rh_tail = NULL;
-  rhc.pg = pg;
-  rhc.status = GNUNET_OK;
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_set_zero (pg->currency,
-                                        &rhc.balance_in));
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_set_zero (pg->currency,
-                                        &rhc.balance_out));
-  qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */
-  for (unsigned int i = 0; NULL != work[i].cb; i++)
-  {
-    qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
-                                               work[i].statement,
-                                               params,
-                                               work[i].cb,
-                                               &rhc);
-    if ( (0 > qs) ||
-         (GNUNET_OK != rhc.status) )
-      break;
-  }
-  if ( (qs < 0) ||
-       (rhc.status != GNUNET_OK) )
-  {
-    TEH_COMMON_free_reserve_history (cls,
-                                     rhc.rh);
-    rhc.rh = NULL;
-    if (qs >= 0)
-    {
-      /* status == SYSERR is a very hard error... */
-      qs = GNUNET_DB_STATUS_HARD_ERROR;
-    }
-  }
-  *rhp = rhc.rh;
-  *balance_in = rhc.balance_in;
-  *balance_out = rhc.balance_out;
-  return qs;
-}
-
-
 /**
  * Get the balance of the specified reserve.
  *
@@ -14139,8 +13114,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
   plugin->do_recoup = &postgres_do_recoup;
   plugin->do_recoup_refresh = &postgres_do_recoup_refresh;
   plugin->get_reserve_balance = &postgres_get_reserve_balance;
-  plugin->get_reserve_history = &postgres_get_reserve_history;
-  plugin->get_reserve_status = &postgres_get_reserve_status;
   plugin->count_known_coins = &postgres_count_known_coins;
   plugin->ensure_coin_known = &postgres_ensure_coin_known;
   plugin->get_known_coin = &postgres_get_known_coin;
@@ -14352,6 +13325,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
     = &TEH_PG_get_coin_transactions;
   plugin->get_expired_reserves
     = &TEH_PG_get_expired_reserves;
+  plugin->get_reserve_history
+    = &TEH_PG_get_reserve_history;
+  plugin->get_reserve_status
+    = &TEH_PG_get_reserve_status;
   plugin->get_unfinished_close_requests
     = &TEH_PG_get_unfinished_close_requests;
   plugin->insert_records_by_table
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index a861563b..df992c6e 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -3834,6 +3834,51 @@ TALER_exchange_online_melt_confirmation_verify (
   const struct TALER_ExchangeSignatureP *sig);
 
 
+/**
+ * Create exchange purse refund confirmation signature.
+ *
+ * @param scb function to call to create the signature
+ * @param amount_without_fee refunded amount
+ * @param refund_fee refund fee charged
+ * @param coin_pub coin that was refunded
+ * @param purse_pub public key of the expired purse
+ * @param[out] pub where to write the public key
+ * @param[out] sig where to write the signature
+ * @return #TALER_EC_NONE on success
+ */
+enum TALER_ErrorCode
+TALER_exchange_online_purse_refund_sign (
+  TALER_ExchangeSignCallback scb,
+  const struct TALER_Amount *amount_without_fee,
+  const struct TALER_Amount *refund_fee,
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const struct TALER_PurseContractPublicKeyP *purse_pub,
+  struct TALER_ExchangePublicKeyP *pub,
+  struct TALER_ExchangeSignatureP *sig);
+
+
+/**
+ * Verify signature of exchange affirming purse refund
+ * from purse expiration.
+ *
+ * @param amount_without_fee refunded amount
+ * @param refund_fee refund fee charged
+ * @param coin_pub coin that was refunded
+ * @param purse_pub public key of the expired purse
+ * @param pub public key to verify signature against
+ * @param sig signature to verify
+ * @return #GNUNET_OK if the signature is valid
+ */
+enum GNUNET_GenericReturnValue
+TALER_exchange_online_purse_refund_verify (
+  const struct TALER_Amount *amount_without_fee,
+  const struct TALER_Amount *refund_fee,
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const struct TALER_PurseContractPublicKeyP *purse_pub,
+  const struct TALER_ExchangePublicKeyP *pub,
+  const struct TALER_ExchangeSignatureP *sig);
+
+
 /**
  * Create exchange key set signature.
  *
@@ -3860,8 +3905,8 @@ TALER_exchange_online_key_set_sign (
  *
  * @param timestamp time when the key set was issued
  * @param hc hash over all the keys
- * @param pub where to write the public key
- * @param sig where to write the signature
+ * @param pub public key to verify signature against
+ * @param sig signature to verify
  * @return #GNUNET_OK if the signature is valid
  */
 enum GNUNET_GenericReturnValue
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index a12c2ac3..7a0d5c55 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -1270,6 +1270,75 @@ struct TALER_EXCHANGEDB_HistoryRequest
 };
 
 
+/**
+ * Details about a (paid for) reserve open request.
+ */
+struct TALER_EXCHANGEDB_OpenRequest
+{
+  /**
+   * Public key of the reserve the open request was for.
+   */
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+  /**
+   * Fee paid for the request from the reserve.
+   */
+  struct TALER_Amount open_fee;
+
+  /**
+   * When was the request made.
+   */
+  struct GNUNET_TIME_Timestamp request_timestamp;
+
+  /**
+   * How long was the reserve supposed to be open.
+   */
+  struct GNUNET_TIME_Timestamp reserve_expiration;
+
+  /**
+   * Signature by the reserve approving the open request.
+   */
+  struct TALER_ReserveSignatureP reserve_sig;
+
+  /**
+   * How many open purses should be included with the
+   * open reserve?
+   */
+  uint32_t purse_limit;
+
+};
+
+
+/**
+ * Details about an (explicit) reserve close request.
+ */
+struct TALER_EXCHANGEDB_CloseRequest
+{
+  /**
+   * Public key of the reserve the history request was for.
+   */
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+  /**
+   * When was the request made.
+   */
+  struct GNUNET_TIME_Timestamp request_timestamp;
+
+  /**
+   * Hash of the payto://-URI of the target account
+   * for the closure, or all zeros for the reserve
+   * origin account.
+   */
+  struct TALER_PaytoHashP target_account_h_payto;
+
+  /**
+   * Signature by the reserve approving the history request.
+   */
+  struct TALER_ReserveSignatureP reserve_sig;
+
+};
+
+
 /**
  * @brief Types of operations on a reserve.
  */
@@ -1306,7 +1375,17 @@ enum TALER_EXCHANGEDB_ReserveOperation
   /**
    * Event where a wallet paid for a full reserve history.
    */
-  TALER_EXCHANGEDB_RO_HISTORY_REQUEST = 5
+  TALER_EXCHANGEDB_RO_HISTORY_REQUEST = 5,
+
+  /**
+   * Event where a wallet paid to open a reserve for longer.
+   */
+  TALER_EXCHANGEDB_RO_OPEN_REQUEST = 6,
+
+  /**
+   * Event where a wallet requested a reserve to be closed.
+   */
+  TALER_EXCHANGEDB_RO_CLOSE_REQUEST = 7
 };
 
 
@@ -1367,6 +1446,16 @@ struct TALER_EXCHANGEDB_ReserveHistory
      */
     struct TALER_EXCHANGEDB_HistoryRequest *history;
 
+    /**
+     * Details about a (paid for) open reserve request.
+     */
+    struct TALER_EXCHANGEDB_OpenRequest *open_request;
+
+    /**
+     * Details about an (explicit) reserve close request.
+     */
+    struct TALER_EXCHANGEDB_CloseRequest *close_request;
+
   } details;
 
 };
diff --git a/src/util/exchange_signatures.c b/src/util/exchange_signatures.c
index d42f70d6..8ecb3caf 100644
--- a/src/util/exchange_signatures.c
+++ b/src/util/exchange_signatures.c
@@ -1379,6 +1379,100 @@ TALER_exchange_online_purse_created_verify (
 }
 
 
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Response by which the exchange affirms that it has
+ * received funds deposited into a purse.
+ */
+struct TALER_CoinPurseRefundConfirmationPS
+{
+
+  /**
+   * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND
+   */
+  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+  /**
+   * Public key of the purse.
+   */
+  struct TALER_PurseContractPublicKeyP purse_pub;
+
+  /**
+   * Public key of the coin.
+   */
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+
+  /**
+   * How much will be refunded to the purse.
+   */
+  struct TALER_AmountNBO refunded_amount;
+
+  /**
+   * How much was the refund fee.
+   */
+  struct TALER_AmountNBO refund_fee;
+
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+
+enum TALER_ErrorCode
+TALER_exchange_online_purse_refund_sign (
+  TALER_ExchangeSignCallback scb,
+  const struct TALER_Amount *amount_without_fee,
+  const struct TALER_Amount *refund_fee,
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const struct TALER_PurseContractPublicKeyP *purse_pub,
+  struct TALER_ExchangePublicKeyP *pub,
+  struct TALER_ExchangeSignatureP *sig)
+{
+  struct TALER_CoinPurseRefundConfirmationPS dc = {
+    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND),
+    .purpose.size = htonl (sizeof (dc)),
+    .coin_pub = *coin_pub,
+    .purse_pub = *purse_pub,
+  };
+
+  TALER_amount_hton (&dc.refunded_amount,
+                     amount_without_fee);
+  TALER_amount_hton (&dc.refund_fee,
+                     refund_fee);
+  return scb (&dc.purpose,
+              pub,
+              sig);
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_exchange_online_purse_refund_verify (
+  const struct TALER_Amount *amount_without_fee,
+  const struct TALER_Amount *refund_fee,
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const struct TALER_PurseContractPublicKeyP *purse_pub,
+  const struct TALER_ExchangePublicKeyP *pub,
+  const struct TALER_ExchangeSignatureP *sig)
+{
+  struct TALER_CoinPurseRefundConfirmationPS dc = {
+    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND),
+    .purpose.size = htonl (sizeof (dc)),
+    .coin_pub = *coin_pub,
+    .purse_pub = *purse_pub,
+  };
+
+  TALER_amount_hton (&dc.refunded_amount,
+                     amount_without_fee);
+  TALER_amount_hton (&dc.refund_fee,
+                     refund_fee);
+  return
+    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND,
+                                &dc,
+                                &sig->eddsa_signature,
+                                &pub->eddsa_pub);
+}
+
+
 GNUNET_NETWORK_STRUCT_BEGIN
 
 /**
diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c
index c57506bd..1209bb69 100644
--- a/src/util/wallet_signatures.c
+++ b/src/util/wallet_signatures.c
@@ -1422,7 +1422,7 @@ struct TALER_ReserveClosePS
   /**
    * Hash of the payto://-URI of the target account
    * for the closure, or all zeros for the reserve
-   * origina ccount.
+   * origin account.
    */
   struct TALER_PaytoHashP target_account_h_payto;
 

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

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