gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated: -towards returning reserve open


From: gnunet
Subject: [taler-exchange] branch master updated: -towards returning reserve open deposits in coin history
Date: Thu, 13 Oct 2022 22:43:26 +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 1ee69f6f -towards returning reserve open deposits in coin history
1ee69f6f is described below

commit 1ee69f6f1d93349a3e576a86c1d93d23ccec28ce
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Thu Oct 13 22:43:22 2022 +0200

    -towards returning reserve open deposits in coin history
---
 src/exchangedb/Makefile.am                  |   2 +
 src/exchangedb/pg_get_coin_transactions.c   | 866 ++++++++++++++++++++++++++++
 src/exchangedb/pg_get_coin_transactions.h   |  44 ++
 src/exchangedb/plugin_exchangedb_common.c   |  29 +-
 src/exchangedb/plugin_exchangedb_common.h   |  51 ++
 src/exchangedb/plugin_exchangedb_postgres.c | 807 +-------------------------
 src/include/taler_exchangedb_plugin.h       |  38 +-
 7 files changed, 1023 insertions(+), 814 deletions(-)

diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index e4094cd7..059c0136 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -68,8 +68,10 @@ plugin_LTLIBRARIES = \
 endif
 
 libtaler_plugin_exchangedb_postgres_la_SOURCES = \
+  plugin_exchangedb_common.c plugin_exchangedb_common.h \
   plugin_exchangedb_postgres.c pg_helper.h \
   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_unfinished_close_requests.c pg_get_unfinished_close_requests.h \
   pg_insert_close_request.c pg_insert_close_request.h \
diff --git a/src/exchangedb/pg_get_coin_transactions.c 
b/src/exchangedb/pg_get_coin_transactions.c
new file mode 100644
index 00000000..54dce7f6
--- /dev/null
+++ b/src/exchangedb/pg_get_coin_transactions.c
@@ -0,0 +1,866 @@
+/*
+   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_coin_transactions.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_coin_transactions.h"
+#include "pg_helper.h"
+#include "plugin_exchangedb_common.h"
+
+
+/**
+ * Closure for callbacks called from #postgres_get_coin_transactions()
+ */
+struct CoinHistoryContext
+{
+  /**
+   * Head of the coin's history list.
+   */
+  struct TALER_EXCHANGEDB_TransactionList *head;
+
+  /**
+   * Public key of the coin we are building the history for.
+   */
+  const struct TALER_CoinSpendPublicKeyP *coin_pub;
+
+  /**
+   * Closure for all callbacks of this database plugin.
+   */
+  void *db_cls;
+
+  /**
+   * Plugin context.
+   */
+  struct PostgresClosure *pg;
+
+  /**
+   * Set to 'true' if the transaction failed.
+   */
+  bool failed;
+
+  /**
+   * Set to 'true' if we found a deposit or melt (for invariant check).
+   */
+  bool have_deposit_or_melt;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+add_coin_deposit (void *cls,
+                  PGresult *result,
+                  unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+  struct PostgresClosure *pg = chc->pg;
+
+  for (unsigned int i = 0; i < num_results; i++)
+  {
+    struct TALER_EXCHANGEDB_DepositListEntry *deposit;
+    struct TALER_EXCHANGEDB_TransactionList *tl;
+    uint64_t serial_id;
+
+    chc->have_deposit_or_melt = true;
+    deposit = GNUNET_new (struct TALER_EXCHANGEDB_DepositListEntry);
+    {
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
+                                     &deposit->amount_with_fee),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
+                                     &deposit->deposit_fee),
+        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
+                                              &deposit->h_denom_pub),
+        GNUNET_PQ_result_spec_allow_null (
+          GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
+                                                &deposit->h_age_commitment),
+          &deposit->no_age_commitment),
+        GNUNET_PQ_result_spec_timestamp ("wallet_timestamp",
+                                         &deposit->timestamp),
+        GNUNET_PQ_result_spec_timestamp ("refund_deadline",
+                                         &deposit->refund_deadline),
+        GNUNET_PQ_result_spec_timestamp ("wire_deadline",
+                                         &deposit->wire_deadline),
+        GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
+                                              &deposit->merchant_pub),
+        GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+                                              &deposit->h_contract_terms),
+        GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
+                                              &deposit->wire_salt),
+        GNUNET_PQ_result_spec_string ("payto_uri",
+                                      &deposit->receiver_wire_account),
+        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+                                              &deposit->csig),
+        GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
+                                      &serial_id),
+        GNUNET_PQ_result_spec_auto_from_type ("done",
+                                              &deposit->done),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    i))
+      {
+        GNUNET_break (0);
+        GNUNET_free (deposit);
+        chc->failed = true;
+        return;
+      }
+    }
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
+    tl->details.deposit = deposit;
+    tl->serial_id = serial_id;
+    chc->head = tl;
+  }
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+add_coin_purse_deposit (void *cls,
+                        PGresult *result,
+                        unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+  struct PostgresClosure *pg = chc->pg;
+
+  for (unsigned int i = 0; i < num_results; i++)
+  {
+    struct TALER_EXCHANGEDB_PurseDepositListEntry *deposit;
+    struct TALER_EXCHANGEDB_TransactionList *tl;
+    uint64_t serial_id;
+
+    chc->have_deposit_or_melt = true;
+    deposit = GNUNET_new (struct TALER_EXCHANGEDB_PurseDepositListEntry);
+    {
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
+                                     &deposit->amount),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
+                                     &deposit->deposit_fee),
+        GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
+                                              &deposit->purse_pub),
+        GNUNET_PQ_result_spec_uint64 ("purse_deposit_serial_id",
+                                      &serial_id),
+        GNUNET_PQ_result_spec_allow_null (
+          GNUNET_PQ_result_spec_string ("partner_base_url",
+                                        &deposit->exchange_base_url),
+          NULL),
+        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+                                              &deposit->coin_sig),
+        GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
+                                              &deposit->h_age_commitment),
+        GNUNET_PQ_result_spec_bool ("refunded",
+                                    &deposit->refunded),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    i))
+      {
+        GNUNET_break (0);
+        GNUNET_free (deposit);
+        chc->failed = true;
+        return;
+      }
+      deposit->no_age_commitment = GNUNET_is_zero (&deposit->h_age_commitment);
+    }
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_PURSE_DEPOSIT;
+    tl->details.purse_deposit = deposit;
+    tl->serial_id = serial_id;
+    chc->head = tl;
+  }
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+add_coin_melt (void *cls,
+               PGresult *result,
+               unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+  struct PostgresClosure *pg = chc->pg;
+
+  for (unsigned int i = 0; i<num_results; i++)
+  {
+    struct TALER_EXCHANGEDB_MeltListEntry *melt;
+    struct TALER_EXCHANGEDB_TransactionList *tl;
+    uint64_t serial_id;
+
+    chc->have_deposit_or_melt = true;
+    melt = GNUNET_new (struct TALER_EXCHANGEDB_MeltListEntry);
+    {
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        GNUNET_PQ_result_spec_auto_from_type ("rc",
+                                              &melt->rc),
+        /* oldcoin_index not needed */
+        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
+                                              &melt->h_denom_pub),
+        GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
+                                              &melt->coin_sig),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
+                                     &melt->amount_with_fee),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
+                                     &melt->melt_fee),
+        GNUNET_PQ_result_spec_allow_null (
+          GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
+                                                &melt->h_age_commitment),
+          &melt->no_age_commitment),
+        GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
+                                      &serial_id),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    i))
+      {
+        GNUNET_break (0);
+        GNUNET_free (melt);
+        chc->failed = true;
+        return;
+      }
+    }
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_MELT;
+    tl->details.melt = melt;
+    tl->serial_id = serial_id;
+    chc->head = tl;
+  }
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+add_coin_refund (void *cls,
+                 PGresult *result,
+                 unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+  struct PostgresClosure *pg = chc->pg;
+
+  for (unsigned int i = 0; i<num_results; i++)
+  {
+    struct TALER_EXCHANGEDB_RefundListEntry *refund;
+    struct TALER_EXCHANGEDB_TransactionList *tl;
+    uint64_t serial_id;
+
+    refund = GNUNET_new (struct TALER_EXCHANGEDB_RefundListEntry);
+    {
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
+                                              &refund->merchant_pub),
+        GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
+                                              &refund->merchant_sig),
+        GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+                                              &refund->h_contract_terms),
+        GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
+                                      &refund->rtransaction_id),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
+                                     &refund->refund_amount),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
+                                     &refund->refund_fee),
+        GNUNET_PQ_result_spec_uint64 ("refund_serial_id",
+                                      &serial_id),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    i))
+      {
+        GNUNET_break (0);
+        GNUNET_free (refund);
+        chc->failed = true;
+        return;
+      }
+    }
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_REFUND;
+    tl->details.refund = refund;
+    tl->serial_id = serial_id;
+    chc->head = tl;
+  }
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+add_old_coin_recoup (void *cls,
+                     PGresult *result,
+                     unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+  struct PostgresClosure *pg = chc->pg;
+
+  for (unsigned int i = 0; i<num_results; i++)
+  {
+    struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
+    struct TALER_EXCHANGEDB_TransactionList *tl;
+    uint64_t serial_id;
+
+    recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
+    {
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
+                                              &recoup->coin.coin_pub),
+        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+                                              &recoup->coin_sig),
+        GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
+                                              &recoup->coin_blind),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
+                                     &recoup->value),
+        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_uint64 ("recoup_refresh_uuid",
+                                      &serial_id),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    i))
+      {
+        GNUNET_break (0);
+        GNUNET_free (recoup);
+        chc->failed = true;
+        return;
+      }
+      recoup->old_coin_pub = *chc->coin_pub;
+    }
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP;
+    tl->details.old_coin_recoup = recoup;
+    tl->serial_id = serial_id;
+    chc->head = tl;
+  }
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+add_coin_recoup (void *cls,
+                 PGresult *result,
+                 unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+  struct PostgresClosure *pg = chc->pg;
+
+  for (unsigned int i = 0; i<num_results; i++)
+  {
+    struct TALER_EXCHANGEDB_RecoupListEntry *recoup;
+    struct TALER_EXCHANGEDB_TransactionList *tl;
+    uint64_t serial_id;
+
+    recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupListEntry);
+    {
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
+                                              &recoup->reserve_pub),
+        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+                                              &recoup->coin_sig),
+        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
+                                              &recoup->h_denom_pub),
+        GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
+                                              &recoup->coin_blind),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
+                                     &recoup->value),
+        GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
+                                         &recoup->timestamp),
+        GNUNET_PQ_result_spec_uint64 ("recoup_uuid",
+                                      &serial_id),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    i))
+      {
+        GNUNET_break (0);
+        GNUNET_free (recoup);
+        chc->failed = true;
+        return;
+      }
+    }
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_RECOUP;
+    tl->details.recoup = recoup;
+    tl->serial_id = serial_id;
+    chc->head = tl;
+  }
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+add_coin_recoup_refresh (void *cls,
+                         PGresult *result,
+                         unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+  struct PostgresClosure *pg = chc->pg;
+
+  for (unsigned int i = 0; i<num_results; i++)
+  {
+    struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
+    struct TALER_EXCHANGEDB_TransactionList *tl;
+    uint64_t serial_id;
+
+    recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
+    {
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
+                                              &recoup->old_coin_pub),
+        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+                                              &recoup->coin_sig),
+        GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
+                                              &recoup->coin_blind),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
+                                     &recoup->value),
+        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_uint64 ("recoup_refresh_uuid",
+                                      &serial_id),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    i))
+      {
+        GNUNET_break (0);
+        GNUNET_free (recoup);
+        chc->failed = true;
+        return;
+      }
+      recoup->coin.coin_pub = *chc->coin_pub;
+    }
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_RECOUP_REFRESH;
+    tl->details.recoup_refresh = recoup;
+    tl->serial_id = serial_id;
+    chc->head = tl;
+  }
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+add_coin_reserve_open (void *cls,
+                       PGresult *result,
+                       unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+  struct PostgresClosure *pg = chc->pg;
+
+  for (unsigned int i = 0; i<num_results; i++)
+  {
+    struct TALER_EXCHANGEDB_ReserveOpenListEntry *role;
+    struct TALER_EXCHANGEDB_TransactionList *tl;
+    uint64_t serial_id;
+
+    role = GNUNET_new (struct TALER_EXCHANGEDB_ReserveOpenListEntry);
+    {
+      struct GNUNET_PQ_ResultSpec rs[] = {
+        GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
+                                              &role->reserve_sig),
+        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+                                              &role->coin_sig),
+        TALER_PQ_RESULT_SPEC_AMOUNT ("contribution",
+                                     &role->coin_contribution),
+        GNUNET_PQ_result_spec_uint64 ("reserve_open_deposit_uuid",
+                                      &serial_id),
+        GNUNET_PQ_result_spec_end
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_PQ_extract_result (result,
+                                    rs,
+                                    i))
+      {
+        GNUNET_break (0);
+        GNUNET_free (role);
+        chc->failed = true;
+        return;
+      }
+    }
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_RESERVE_OPEN;
+    tl->details.reserve_open = role;
+    tl->serial_id = serial_id;
+    chc->head = tl;
+  }
+}
+
+
+/**
+ * Work we need to do.
+ */
+struct Work
+{
+  /**
+   * SQL prepared statement name.
+   */
+  const char *statement;
+
+  /**
+   * Function to call to handle the result(s).
+   */
+  GNUNET_PQ_PostgresResultHandler cb;
+};
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_coin_transactions (
+  void *cls,
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  struct TALER_EXCHANGEDB_TransactionList **tlp)
+{
+  struct PostgresClosure *pg = cls;
+  static const struct Work work[] = {
+    /** #TALER_EXCHANGEDB_TT_DEPOSIT */
+    { "get_deposit_with_coin_pub",
+      &add_coin_deposit },
+    /** #TALER_EXCHANGEDB_TT_MELT */
+    { "get_refresh_session_by_coin",
+      &add_coin_melt },
+    /** #TALER_EXCHANGEDB_TT_PURSE_DEPOSIT */
+    { "get_purse_deposit_by_coin_pub",
+      &add_coin_purse_deposit },
+    /** #TALER_EXCHANGEDB_TT_REFUND */
+    { "get_refunds_by_coin",
+      &add_coin_refund },
+    /** #TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP */
+    { "recoup_by_old_coin",
+      &add_old_coin_recoup },
+    /** #TALER_EXCHANGEDB_TT_RECOUP */
+    { "recoup_by_coin",
+      &add_coin_recoup },
+    /** #TALER_EXCHANGEDB_TT_RECOUP_REFRESH */
+    { "recoup_by_refreshed_coin",
+      &add_coin_recoup_refresh },
+    /** #TALER_EXCHANGEDB_TT_RESERVE_OPEN */
+    { "reserve_open_by_coin",
+      &add_coin_reserve_open },
+    { NULL, NULL }
+  };
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (coin_pub),
+    GNUNET_PQ_query_param_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+  struct CoinHistoryContext chc = {
+    .head = NULL,
+    .coin_pub = coin_pub,
+    .pg = pg,
+    .db_cls = cls
+  };
+
+  PREPARE (pg,
+           "get_deposit_with_coin_pub",
+           "SELECT"
+           " dep.amount_with_fee_val"
+           ",dep.amount_with_fee_frac"
+           ",denoms.fee_deposit_val"
+           ",denoms.fee_deposit_frac"
+           ",denoms.denom_pub_hash"
+           ",kc.age_commitment_hash"
+           ",dep.wallet_timestamp"
+           ",dep.refund_deadline"
+           ",dep.wire_deadline"
+           ",dep.merchant_pub"
+           ",dep.h_contract_terms"
+           ",dep.wire_salt"
+           ",wt.payto_uri"
+           ",dep.coin_sig"
+           ",dep.deposit_serial_id"
+           ",dep.done"
+           " FROM deposits dep"
+           "    JOIN wire_targets wt"
+           "      USING (wire_target_h_payto)"
+           "    JOIN known_coins kc"
+           "      ON (kc.coin_pub = dep.coin_pub)"
+           "    JOIN denominations denoms"
+           "      USING (denominations_serial)"
+           " WHERE dep.coin_pub=$1;");
+  PREPARE (pg,
+           "get_refresh_session_by_coin",
+           "SELECT"
+           " rc"
+           ",old_coin_sig"
+           ",amount_with_fee_val"
+           ",amount_with_fee_frac"
+           ",denoms.denom_pub_hash"
+           ",denoms.fee_refresh_val"
+           ",denoms.fee_refresh_frac"
+           ",kc.age_commitment_hash"
+           ",melt_serial_id"
+           " FROM refresh_commitments"
+           " JOIN known_coins kc"
+           "   ON (refresh_commitments.old_coin_pub = kc.coin_pub)"
+           " JOIN denominations denoms"
+           "   USING (denominations_serial)"
+           " WHERE old_coin_pub=$1;");
+  PREPARE (pg,
+           "get_purse_deposit_by_coin_pub",
+           "SELECT"
+           " partner_base_url"
+           ",pd.amount_with_fee_val"
+           ",pd.amount_with_fee_frac"
+           ",denoms.fee_deposit_val"
+           ",denoms.fee_deposit_frac"
+           ",pd.purse_pub"
+           ",kc.age_commitment_hash"
+           ",pd.coin_sig"
+           ",pd.purse_deposit_serial_id"
+           ",pr.refunded"
+           " FROM purse_deposits pd"
+           " LEFT JOIN partners"
+           "   USING (partner_serial_id)"
+           " JOIN purse_requests pr"
+           "   USING (purse_pub)"
+           " JOIN known_coins kc"
+           "   ON (pd.coin_pub = kc.coin_pub)"
+           " JOIN denominations denoms"
+           "   USING (denominations_serial)"
+           // FIXME: use to-be-created materialized index
+           // on coin_pub (query crosses partitions!)
+           " WHERE pd.coin_pub=$1;");
+  PREPARE (pg,
+           "get_refunds_by_coin",
+           "SELECT"
+           " dep.merchant_pub"
+           ",ref.merchant_sig"
+           ",dep.h_contract_terms"
+           ",ref.rtransaction_id"
+           ",ref.amount_with_fee_val"
+           ",ref.amount_with_fee_frac"
+           ",denom.fee_refund_val "
+           ",denom.fee_refund_frac "
+           ",ref.refund_serial_id"
+           " FROM refunds ref"
+           " JOIN deposits dep"
+           "   ON (ref.coin_pub = dep.coin_pub AND ref.deposit_serial_id = 
dep.deposit_serial_id)"
+           " JOIN known_coins kc"
+           "   ON (ref.coin_pub = kc.coin_pub)"
+           " JOIN denominations denom"
+           "   USING (denominations_serial)"
+           " WHERE ref.coin_pub=$1;");
+  PREPARE (pg,
+           "recoup_by_old_coin",
+           "SELECT"
+           " coins.coin_pub"
+           ",coin_sig"
+           ",coin_blind"
+           ",amount_val"
+           ",amount_frac"
+           ",recoup_timestamp"
+           ",denoms.denom_pub_hash"
+           ",coins.denom_sig"
+           ",recoup_refresh_uuid"
+           " FROM recoup_refresh"
+           " JOIN known_coins coins"
+           "   USING (coin_pub)"
+           " JOIN denominations denoms"
+           "   USING (denominations_serial)"
+           " WHERE rrc_serial IN"
+           "   (SELECT rrc.rrc_serial"
+           "    FROM refresh_commitments"
+           "       JOIN refresh_revealed_coins rrc"
+           "           USING (melt_serial_id)"
+           "    WHERE old_coin_pub=$1);");
+  PREPARE (pg,
+           "recoup_by_coin",
+           "SELECT"
+           " reserves.reserve_pub"
+           ",denoms.denom_pub_hash"
+           ",coin_sig"
+           ",coin_blind"
+           ",amount_val"
+           ",amount_frac"
+           ",recoup_timestamp"
+           ",recoup_uuid"
+           " FROM recoup rcp"
+           /* NOTE: suboptimal JOIN follows: crosses shards!
+              Could theoretically be improved via a materialized
+              index. But likely not worth it (query is rare and
+              number of reserve shards might be limited) */
+           " JOIN reserves_out ro"
+           "   USING (reserve_out_serial_id)"
+           " JOIN reserves"
+           "   USING (reserve_uuid)"
+           " JOIN known_coins coins"
+           "   USING (coin_pub)"
+           " JOIN denominations denoms"
+           "   ON (denoms.denominations_serial = coins.denominations_serial)"
+           " WHERE coins.coin_pub=$1;");
+  /* Used in #postgres_get_coin_transactions() to obtain recoup transactions
+     for a refreshed coin */
+  PREPARE (pg,
+           "recoup_by_refreshed_coin",
+           "SELECT"
+           " old_coins.coin_pub AS old_coin_pub"
+           ",coin_sig"
+           ",coin_blind"
+           ",amount_val"
+           ",amount_frac"
+           ",recoup_timestamp"
+           ",denoms.denom_pub_hash"
+           ",coins.denom_sig"
+           ",recoup_refresh_uuid"
+           " FROM recoup_refresh"
+           "    JOIN refresh_revealed_coins rrc"
+           "      USING (rrc_serial)"
+           "    JOIN refresh_commitments rfc"
+           "      ON (rrc.melt_serial_id = rfc.melt_serial_id)"
+           "    JOIN known_coins old_coins"
+           "      ON (rfc.old_coin_pub = old_coins.coin_pub)"
+           "    JOIN known_coins coins"
+           "      ON (recoup_refresh.coin_pub = coins.coin_pub)"
+           "    JOIN denominations denoms"
+           "      ON (denoms.denominations_serial = 
coins.denominations_serial)"
+           " WHERE coins.coin_pub=$1;");
+  PREPARE (pg,
+           "reserve_open_by_coin",
+           "SELECT"
+           " reserve_open_deposit_uuid"
+           ",coin_sig"
+           ",reserve_sig"
+           ",contribution_val"
+           ",contribution_frac"
+           " FROM reserves_open_deposits"
+           " WHERE coin_pub=$1;");
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Getting transactions for coin %s\n",
+              TALER_B2S (coin_pub));
+  for (unsigned int i = 0; NULL != work[i].statement; i++)
+  {
+    qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                               work[i].statement,
+                                               params,
+                                               work[i].cb,
+                                               &chc);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Coin %s yielded %d transactions of type %s\n",
+                TALER_B2S (coin_pub),
+                qs,
+                work[i].statement);
+    if ( (0 > qs) ||
+         (chc.failed) )
+    {
+      if (NULL != chc.head)
+        TEH_COMMON_free_coin_transaction_list (cls,
+                                               chc.head);
+      *tlp = NULL;
+      if (chc.failed)
+        qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return qs;
+    }
+  }
+  *tlp = chc.head;
+  if (NULL == chc.head)
+    return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+}
diff --git a/src/exchangedb/pg_get_coin_transactions.h 
b/src/exchangedb/pg_get_coin_transactions.h
new file mode 100644
index 00000000..c95fd094
--- /dev/null
+++ b/src/exchangedb/pg_get_coin_transactions.h
@@ -0,0 +1,44 @@
+/*
+   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_coin_transactions.h
+ * @brief implementation of the get_coin_transactions function
+ * @author Christian Grothoff
+ */
+#ifndef PG_GET_COIN_TRANSACTIONS_H
+#define PG_GET_COIN_TRANSACTIONS_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Compile a list of all (historic) transactions performed with the given coin
+ * (/refresh/melt, /deposit, /refund and /recoup operations).
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param coin_pub coin to investigate
+ * @param[out] tlp set to list of transactions, NULL if coin is fresh
+ * @return database transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_coin_transactions (
+  void *cls,
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  struct TALER_EXCHANGEDB_TransactionList **tlp);
+
+#endif
diff --git a/src/exchangedb/plugin_exchangedb_common.c 
b/src/exchangedb/plugin_exchangedb_common.c
index ca590350..10263adf 100644
--- a/src/exchangedb/plugin_exchangedb_common.c
+++ b/src/exchangedb/plugin_exchangedb_common.c
@@ -19,16 +19,14 @@
  *        included in each plugin.
  * @author Christian Grothoff
  */
+#include "platform.h"
+#include "plugin_exchangedb_common.h"
 
-/**
- * Free memory associated with the given reserve history.
- *
- * @param cls the @e cls of this struct with the plugin-specific state (unused)
- * @param rh history to free.
- */
-static void
-common_free_reserve_history (void *cls,
-                             struct TALER_EXCHANGEDB_ReserveHistory *rh)
+
+void
+TEH_COMMON_free_reserve_history (
+  void *cls,
+  struct TALER_EXCHANGEDB_ReserveHistory *rh)
 {
   (void) cls;
   while (NULL != rh)
@@ -99,15 +97,10 @@ common_free_reserve_history (void *cls,
 }
 
 
-/**
- * Free linked list of transactions.
- *
- * @param cls the @e cls of this struct with the plugin-specific state (unused)
- * @param tl list to free
- */
-static void
-common_free_coin_transaction_list (void *cls,
-                                   struct TALER_EXCHANGEDB_TransactionList *tl)
+void
+TEH_COMMON_free_coin_transaction_list (
+  void *cls,
+  struct TALER_EXCHANGEDB_TransactionList *tl)
 {
   (void) cls;
   while (NULL != tl)
diff --git a/src/exchangedb/plugin_exchangedb_common.h 
b/src/exchangedb/plugin_exchangedb_common.h
new file mode 100644
index 00000000..0355c44a
--- /dev/null
+++ b/src/exchangedb/plugin_exchangedb_common.h
@@ -0,0 +1,51 @@
+/*
+   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 plugin_exchangedb_common.h
+ * @brief implementation of database-independent functions
+ * @author Christian Grothoff
+ */
+#ifndef PLUGIN_EXCHANGEDB_COMMON_H
+#define PLUGIN_EXCHANGEDB_COMMON_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+/**
+ * Free memory associated with the given reserve history.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state (unused)
+ * @param[in] rh history to free.
+ */
+void
+TEH_COMMON_free_reserve_history (
+  void *cls,
+  struct TALER_EXCHANGEDB_ReserveHistory *rh);
+
+
+/**
+ * Free linked list of transactions.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state (unused)
+ * @param[in] tl list to free
+ */
+void
+TEH_COMMON_free_coin_transaction_list (
+  void *cls,
+  struct TALER_EXCHANGEDB_TransactionList *tl);
+
+#endif
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 9bf42155..940523b7 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -29,8 +29,10 @@
 #include "taler_util.h"
 #include "taler_json_lib.h"
 #include "taler_exchangedb_plugin.h"
+#include "plugin_exchangedb_common.h"
 #include "pg_helper.h"
 #include "pg_do_reserve_open.h"
+#include "pg_get_coin_transactions.h"
 #include "pg_get_expired_reserves.h"
 #include "pg_get_unfinished_close_requests.h"
 #include "pg_insert_close_request.h"
@@ -45,7 +47,6 @@
 #include <pthread.h>
 #include <libpq-fe.h>
 
-#include "plugin_exchangedb_common.c"
 
 /**
  * Set to 1 to enable Postgres auto_explain module. This will
@@ -1177,53 +1178,6 @@ prepare_statements (struct PostgresClosure *pg)
       "     ON (kc.denominations_serial = denom.denominations_serial)"
       " WHERE melt_serial_id>=$1"
       " ORDER BY melt_serial_id ASC;"),
-    /* Query the 'refresh_commitments' by coin public key,
-       used in #postgres_get_coin_transactions() */
-    GNUNET_PQ_make_prepare (
-      "get_refresh_session_by_coin",
-      "SELECT"
-      " rc"
-      ",old_coin_sig"
-      ",amount_with_fee_val"
-      ",amount_with_fee_frac"
-      ",denoms.denom_pub_hash"
-      ",denoms.fee_refresh_val"
-      ",denoms.fee_refresh_frac"
-      ",kc.age_commitment_hash"
-      ",melt_serial_id"
-      " FROM refresh_commitments"
-      " JOIN known_coins kc"
-      "   ON (refresh_commitments.old_coin_pub = kc.coin_pub)"
-      " JOIN denominations denoms"
-      "   USING (denominations_serial)"
-      " WHERE old_coin_pub=$1;"),
-    /* Find purse deposits by coin,
-       used in #postgres_get_coin_transactions() */
-    GNUNET_PQ_make_prepare (
-      "get_purse_deposit_by_coin_pub",
-      "SELECT"
-      " partner_base_url"
-      ",pd.amount_with_fee_val"
-      ",pd.amount_with_fee_frac"
-      ",denoms.fee_deposit_val"
-      ",denoms.fee_deposit_frac"
-      ",pd.purse_pub"
-      ",kc.age_commitment_hash"
-      ",pd.coin_sig"
-      ",pd.purse_deposit_serial_id"
-      ",pr.refunded"
-      " FROM purse_deposits pd"
-      " LEFT JOIN partners"
-      "   USING (partner_serial_id)"
-      " JOIN purse_requests pr"
-      "   USING (purse_pub)"
-      " JOIN known_coins kc"
-      "   ON (pd.coin_pub = kc.coin_pub)"
-      " JOIN denominations denoms"
-      "   USING (denominations_serial)"
-      // FIXME: use to-be-created materialized index
-      // on coin_pub (query crosses partitions!)
-      " WHERE pd.coin_pub=$1;"),
     /* Store information about the desired denominations for a
        refresh operation, used in #postgres_insert_refresh_reveal() */
     GNUNET_PQ_make_prepare (
@@ -1287,26 +1241,6 @@ prepare_statements (struct PostgresClosure *pg)
       "     AND h_contract_terms=$4"
       "     AND merchant_pub=$2"),
     /* Query the 'refunds' by coin public key */
-    GNUNET_PQ_make_prepare (
-      "get_refunds_by_coin",
-      "SELECT"
-      " dep.merchant_pub"
-      ",ref.merchant_sig"
-      ",dep.h_contract_terms"
-      ",ref.rtransaction_id"
-      ",ref.amount_with_fee_val"
-      ",ref.amount_with_fee_frac"
-      ",denom.fee_refund_val "
-      ",denom.fee_refund_frac "
-      ",ref.refund_serial_id"
-      " FROM refunds ref"
-      " JOIN deposits dep"
-      "   ON (ref.coin_pub = dep.coin_pub AND ref.deposit_serial_id = 
dep.deposit_serial_id)"
-      " JOIN known_coins kc"
-      "   ON (ref.coin_pub = kc.coin_pub)"
-      " JOIN denominations denom"
-      "   USING (denominations_serial)"
-      " WHERE ref.coin_pub=$1;"),
     /* Query the 'refunds' by coin public key, merchant_pub and contract hash 
*/
     GNUNET_PQ_make_prepare (
       "get_refunds_by_coin_and_contract",
@@ -1727,36 +1661,6 @@ prepare_statements (struct PostgresClosure *pg)
       " WHERE wire_target_h_payto=$1"
       "   AND wtid_raw=$2"),
 
-    /* Used in #postgres_get_coin_transactions() to obtain information
-       about how a coin has been spend with /deposit requests. */
-    GNUNET_PQ_make_prepare (
-      "get_deposit_with_coin_pub",
-      "SELECT"
-      " dep.amount_with_fee_val"
-      ",dep.amount_with_fee_frac"
-      ",denoms.fee_deposit_val"
-      ",denoms.fee_deposit_frac"
-      ",denoms.denom_pub_hash"
-      ",kc.age_commitment_hash"
-      ",dep.wallet_timestamp"
-      ",dep.refund_deadline"
-      ",dep.wire_deadline"
-      ",dep.merchant_pub"
-      ",dep.h_contract_terms"
-      ",dep.wire_salt"
-      ",wt.payto_uri"
-      ",dep.coin_sig"
-      ",dep.deposit_serial_id"
-      ",dep.done"
-      " FROM deposits dep"
-      "    JOIN wire_targets wt"
-      "      USING (wire_target_h_payto)"
-      "    JOIN known_coins kc"
-      "      ON (kc.coin_pub = dep.coin_pub)"
-      "    JOIN denominations denoms"
-      "      USING (denominations_serial)"
-      " WHERE dep.coin_pub=$1;"),
-
     /* Used in #postgres_get_link_data(). */
     GNUNET_PQ_make_prepare (
       "get_link",
@@ -2212,31 +2116,6 @@ prepare_statements (struct PostgresClosure *pg)
       "  JOIN exchange_do_recoup_by_reserve($1) robr"
       "    USING (denominations_serial)"
       " WHERE recoup_timestamp>=$2;"),
-    /* Used in #postgres_get_coin_transactions() to obtain recoup transactions
-       affecting old coins of refreshed coins */
-    GNUNET_PQ_make_prepare (
-      "recoup_by_old_coin",
-      "SELECT"
-      " coins.coin_pub"
-      ",coin_sig"
-      ",coin_blind"
-      ",amount_val"
-      ",amount_frac"
-      ",recoup_timestamp"
-      ",denoms.denom_pub_hash"
-      ",coins.denom_sig"
-      ",recoup_refresh_uuid"
-      " FROM recoup_refresh"
-      " JOIN known_coins coins"
-      "   USING (coin_pub)"
-      " JOIN denominations denoms"
-      "   USING (denominations_serial)"
-      " WHERE rrc_serial IN"
-      "   (SELECT rrc.rrc_serial"
-      "    FROM refresh_commitments"
-      "       JOIN refresh_revealed_coins rrc"
-      "           USING (melt_serial_id)"
-      "    WHERE old_coin_pub=$1);"),
     /* Used in #postgres_get_reserve_history() */
     GNUNET_PQ_make_prepare (
       "close_by_reserve",
@@ -2348,57 +2227,6 @@ prepare_statements (struct PostgresClosure *pg)
       "  AND request_timestamp>=$2;"),
     /* Used in #postgres_get_coin_transactions() to obtain recoup transactions
        for a coin */
-    GNUNET_PQ_make_prepare (
-      "recoup_by_coin",
-      "SELECT"
-      " reserves.reserve_pub"
-      ",denoms.denom_pub_hash"
-      ",coin_sig"
-      ",coin_blind"
-      ",amount_val"
-      ",amount_frac"
-      ",recoup_timestamp"
-      ",recoup_uuid"
-      " FROM recoup rcp"
-      /* NOTE: suboptimal JOIN follows: crosses shards!
-         Could theoretically be improved via a materialized
-         index. But likely not worth it (query is rare and
-         number of reserve shards might be limited) */
-      " JOIN reserves_out ro"
-      "   USING (reserve_out_serial_id)"
-      " JOIN reserves"
-      "   USING (reserve_uuid)"
-      " JOIN known_coins coins"
-      "   USING (coin_pub)"
-      " JOIN denominations denoms"
-      "   ON (denoms.denominations_serial = coins.denominations_serial)"
-      " WHERE coins.coin_pub=$1;"),
-    /* Used in #postgres_get_coin_transactions() to obtain recoup transactions
-       for a refreshed coin */
-    GNUNET_PQ_make_prepare (
-      "recoup_by_refreshed_coin",
-      "SELECT"
-      " old_coins.coin_pub AS old_coin_pub"
-      ",coin_sig"
-      ",coin_blind"
-      ",amount_val"
-      ",amount_frac"
-      ",recoup_timestamp"
-      ",denoms.denom_pub_hash"
-      ",coins.denom_sig"
-      ",recoup_refresh_uuid"
-      " FROM recoup_refresh"
-      "    JOIN refresh_revealed_coins rrc"
-      "      USING (rrc_serial)"
-      "    JOIN refresh_commitments rfc"
-      "      ON (rrc.melt_serial_id = rfc.melt_serial_id)"
-      "    JOIN known_coins old_coins"
-      "      ON (rfc.old_coin_pub = old_coins.coin_pub)"
-      "    JOIN known_coins coins"
-      "      ON (recoup_refresh.coin_pub = coins.coin_pub)"
-      "    JOIN denominations denoms"
-      "      ON (denoms.denominations_serial = coins.denominations_serial)"
-      " WHERE coins.coin_pub=$1;"),
     /* Used in #postgres_get_reserve_by_h_blind() */
     GNUNET_PQ_make_prepare (
       "reserve_by_h_blind",
@@ -5580,8 +5408,8 @@ postgres_get_reserve_history (void *cls,
   if ( (qs < 0) ||
        (rhc.status != GNUNET_OK) )
   {
-    common_free_reserve_history (cls,
-                                 rhc.rh);
+    TEH_COMMON_free_reserve_history (cls,
+                                     rhc.rh);
     rhc.rh = NULL;
     if (qs >= 0)
     {
@@ -5691,8 +5519,8 @@ postgres_get_reserve_status (void *cls,
   if ( (qs < 0) ||
        (rhc.status != GNUNET_OK) )
   {
-    common_free_reserve_history (cls,
-                                 rhc.rh);
+    TEH_COMMON_free_reserve_history (cls,
+                                     rhc.rh);
     rhc.rh = NULL;
     if (qs >= 0)
     {
@@ -7150,620 +6978,6 @@ postgres_get_link_data (void *cls,
 }
 
 
-/**
- * Closure for callbacks called from #postgres_get_coin_transactions()
- */
-struct CoinHistoryContext
-{
-  /**
-   * Head of the coin's history list.
-   */
-  struct TALER_EXCHANGEDB_TransactionList *head;
-
-  /**
-   * Public key of the coin we are building the history for.
-   */
-  const struct TALER_CoinSpendPublicKeyP *coin_pub;
-
-  /**
-   * Closure for all callbacks of this database plugin.
-   */
-  void *db_cls;
-
-  /**
-   * Plugin context.
-   */
-  struct PostgresClosure *pg;
-
-  /**
-   * Set to 'true' if the transaction failed.
-   */
-  bool failed;
-
-  /**
-   * Set to 'true' if we found a deposit or melt (for invariant check).
-   */
-  bool have_deposit_or_melt;
-};
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct CoinHistoryContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-add_coin_deposit (void *cls,
-                  PGresult *result,
-                  unsigned int num_results)
-{
-  struct CoinHistoryContext *chc = cls;
-  struct PostgresClosure *pg = chc->pg;
-
-  for (unsigned int i = 0; i < num_results; i++)
-  {
-    struct TALER_EXCHANGEDB_DepositListEntry *deposit;
-    struct TALER_EXCHANGEDB_TransactionList *tl;
-    uint64_t serial_id;
-
-    chc->have_deposit_or_melt = true;
-    deposit = GNUNET_new (struct TALER_EXCHANGEDB_DepositListEntry);
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
-                                     &deposit->amount_with_fee),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
-                                     &deposit->deposit_fee),
-        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
-                                              &deposit->h_denom_pub),
-        GNUNET_PQ_result_spec_allow_null (
-          GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
-                                                &deposit->h_age_commitment),
-          &deposit->no_age_commitment),
-        GNUNET_PQ_result_spec_timestamp ("wallet_timestamp",
-                                         &deposit->timestamp),
-        GNUNET_PQ_result_spec_timestamp ("refund_deadline",
-                                         &deposit->refund_deadline),
-        GNUNET_PQ_result_spec_timestamp ("wire_deadline",
-                                         &deposit->wire_deadline),
-        GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
-                                              &deposit->merchant_pub),
-        GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
-                                              &deposit->h_contract_terms),
-        GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
-                                              &deposit->wire_salt),
-        GNUNET_PQ_result_spec_string ("payto_uri",
-                                      &deposit->receiver_wire_account),
-        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
-                                              &deposit->csig),
-        GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
-                                      &serial_id),
-        GNUNET_PQ_result_spec_auto_from_type ("done",
-                                              &deposit->done),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    i))
-      {
-        GNUNET_break (0);
-        GNUNET_free (deposit);
-        chc->failed = true;
-        return;
-      }
-    }
-    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-    tl->next = chc->head;
-    tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
-    tl->details.deposit = deposit;
-    tl->serial_id = serial_id;
-    chc->head = tl;
-  }
-}
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct CoinHistoryContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-add_coin_purse_deposit (void *cls,
-                        PGresult *result,
-                        unsigned int num_results)
-{
-  struct CoinHistoryContext *chc = cls;
-  struct PostgresClosure *pg = chc->pg;
-
-  for (unsigned int i = 0; i < num_results; i++)
-  {
-    struct TALER_EXCHANGEDB_PurseDepositListEntry *deposit;
-    struct TALER_EXCHANGEDB_TransactionList *tl;
-    uint64_t serial_id;
-
-    chc->have_deposit_or_melt = true;
-    deposit = GNUNET_new (struct TALER_EXCHANGEDB_PurseDepositListEntry);
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
-                                     &deposit->amount),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
-                                     &deposit->deposit_fee),
-        GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
-                                              &deposit->purse_pub),
-        GNUNET_PQ_result_spec_uint64 ("purse_deposit_serial_id",
-                                      &serial_id),
-        GNUNET_PQ_result_spec_allow_null (
-          GNUNET_PQ_result_spec_string ("partner_base_url",
-                                        &deposit->exchange_base_url),
-          NULL),
-        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
-                                              &deposit->coin_sig),
-        GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
-                                              &deposit->h_age_commitment),
-        GNUNET_PQ_result_spec_bool ("refunded",
-                                    &deposit->refunded),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    i))
-      {
-        GNUNET_break (0);
-        GNUNET_free (deposit);
-        chc->failed = true;
-        return;
-      }
-      deposit->no_age_commitment = GNUNET_is_zero (&deposit->h_age_commitment);
-    }
-    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-    tl->next = chc->head;
-    tl->type = TALER_EXCHANGEDB_TT_PURSE_DEPOSIT;
-    tl->details.purse_deposit = deposit;
-    tl->serial_id = serial_id;
-    chc->head = tl;
-  }
-}
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct CoinHistoryContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-add_coin_melt (void *cls,
-               PGresult *result,
-               unsigned int num_results)
-{
-  struct CoinHistoryContext *chc = cls;
-  struct PostgresClosure *pg = chc->pg;
-
-  for (unsigned int i = 0; i<num_results; i++)
-  {
-    struct TALER_EXCHANGEDB_MeltListEntry *melt;
-    struct TALER_EXCHANGEDB_TransactionList *tl;
-    uint64_t serial_id;
-
-    chc->have_deposit_or_melt = true;
-    melt = GNUNET_new (struct TALER_EXCHANGEDB_MeltListEntry);
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        GNUNET_PQ_result_spec_auto_from_type ("rc",
-                                              &melt->rc),
-        /* oldcoin_index not needed */
-        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
-                                              &melt->h_denom_pub),
-        GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
-                                              &melt->coin_sig),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
-                                     &melt->amount_with_fee),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
-                                     &melt->melt_fee),
-        GNUNET_PQ_result_spec_allow_null (
-          GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
-                                                &melt->h_age_commitment),
-          &melt->no_age_commitment),
-        GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
-                                      &serial_id),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    i))
-      {
-        GNUNET_break (0);
-        GNUNET_free (melt);
-        chc->failed = true;
-        return;
-      }
-    }
-    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-    tl->next = chc->head;
-    tl->type = TALER_EXCHANGEDB_TT_MELT;
-    tl->details.melt = melt;
-    tl->serial_id = serial_id;
-    chc->head = tl;
-  }
-}
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct CoinHistoryContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-add_coin_refund (void *cls,
-                 PGresult *result,
-                 unsigned int num_results)
-{
-  struct CoinHistoryContext *chc = cls;
-  struct PostgresClosure *pg = chc->pg;
-
-  for (unsigned int i = 0; i<num_results; i++)
-  {
-    struct TALER_EXCHANGEDB_RefundListEntry *refund;
-    struct TALER_EXCHANGEDB_TransactionList *tl;
-    uint64_t serial_id;
-
-    refund = GNUNET_new (struct TALER_EXCHANGEDB_RefundListEntry);
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
-                                              &refund->merchant_pub),
-        GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
-                                              &refund->merchant_sig),
-        GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
-                                              &refund->h_contract_terms),
-        GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
-                                      &refund->rtransaction_id),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
-                                     &refund->refund_amount),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
-                                     &refund->refund_fee),
-        GNUNET_PQ_result_spec_uint64 ("refund_serial_id",
-                                      &serial_id),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    i))
-      {
-        GNUNET_break (0);
-        GNUNET_free (refund);
-        chc->failed = true;
-        return;
-      }
-    }
-    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-    tl->next = chc->head;
-    tl->type = TALER_EXCHANGEDB_TT_REFUND;
-    tl->details.refund = refund;
-    tl->serial_id = serial_id;
-    chc->head = tl;
-  }
-}
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct CoinHistoryContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-add_old_coin_recoup (void *cls,
-                     PGresult *result,
-                     unsigned int num_results)
-{
-  struct CoinHistoryContext *chc = cls;
-  struct PostgresClosure *pg = chc->pg;
-
-  for (unsigned int i = 0; i<num_results; i++)
-  {
-    struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
-    struct TALER_EXCHANGEDB_TransactionList *tl;
-    uint64_t serial_id;
-
-    recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
-                                              &recoup->coin.coin_pub),
-        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
-                                              &recoup->coin_sig),
-        GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
-                                              &recoup->coin_blind),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
-                                     &recoup->value),
-        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_uint64 ("recoup_refresh_uuid",
-                                      &serial_id),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    i))
-      {
-        GNUNET_break (0);
-        GNUNET_free (recoup);
-        chc->failed = true;
-        return;
-      }
-      recoup->old_coin_pub = *chc->coin_pub;
-    }
-    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-    tl->next = chc->head;
-    tl->type = TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP;
-    tl->details.old_coin_recoup = recoup;
-    tl->serial_id = serial_id;
-    chc->head = tl;
-  }
-}
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct CoinHistoryContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-add_coin_recoup (void *cls,
-                 PGresult *result,
-                 unsigned int num_results)
-{
-  struct CoinHistoryContext *chc = cls;
-  struct PostgresClosure *pg = chc->pg;
-
-  for (unsigned int i = 0; i<num_results; i++)
-  {
-    struct TALER_EXCHANGEDB_RecoupListEntry *recoup;
-    struct TALER_EXCHANGEDB_TransactionList *tl;
-    uint64_t serial_id;
-
-    recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupListEntry);
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
-                                              &recoup->reserve_pub),
-        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
-                                              &recoup->coin_sig),
-        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
-                                              &recoup->h_denom_pub),
-        GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
-                                              &recoup->coin_blind),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
-                                     &recoup->value),
-        GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
-                                         &recoup->timestamp),
-        GNUNET_PQ_result_spec_uint64 ("recoup_uuid",
-                                      &serial_id),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    i))
-      {
-        GNUNET_break (0);
-        GNUNET_free (recoup);
-        chc->failed = true;
-        return;
-      }
-    }
-    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-    tl->next = chc->head;
-    tl->type = TALER_EXCHANGEDB_TT_RECOUP;
-    tl->details.recoup = recoup;
-    tl->serial_id = serial_id;
-    chc->head = tl;
-  }
-}
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct CoinHistoryContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-add_coin_recoup_refresh (void *cls,
-                         PGresult *result,
-                         unsigned int num_results)
-{
-  struct CoinHistoryContext *chc = cls;
-  struct PostgresClosure *pg = chc->pg;
-
-  for (unsigned int i = 0; i<num_results; i++)
-  {
-    struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
-    struct TALER_EXCHANGEDB_TransactionList *tl;
-    uint64_t serial_id;
-
-    recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
-                                              &recoup->old_coin_pub),
-        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
-                                              &recoup->coin_sig),
-        GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
-                                              &recoup->coin_blind),
-        TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
-                                     &recoup->value),
-        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_uint64 ("recoup_refresh_uuid",
-                                      &serial_id),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    i))
-      {
-        GNUNET_break (0);
-        GNUNET_free (recoup);
-        chc->failed = true;
-        return;
-      }
-      recoup->coin.coin_pub = *chc->coin_pub;
-    }
-    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-    tl->next = chc->head;
-    tl->type = TALER_EXCHANGEDB_TT_RECOUP_REFRESH;
-    tl->details.recoup_refresh = recoup;
-    tl->serial_id = serial_id;
-    chc->head = tl;
-  }
-}
-
-
-/**
- * Work we need to do.
- */
-struct Work
-{
-  /**
-   * SQL prepared statement name.
-   */
-  const char *statement;
-
-  /**
-   * Function to call to handle the result(s).
-   */
-  GNUNET_PQ_PostgresResultHandler cb;
-};
-
-
-/**
- * Compile a list of all (historic) transactions performed with the given coin
- * (/refresh/melt, /deposit, /refund and /recoup operations).
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param coin_pub coin to investigate
- * @param[out] tlp set to list of transactions, NULL if coin is fresh
- * @return database transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_coin_transactions (
-  void *cls,
-  const struct TALER_CoinSpendPublicKeyP *coin_pub,
-  struct TALER_EXCHANGEDB_TransactionList **tlp)
-{
-  struct PostgresClosure *pg = cls;
-  static const struct Work work[] = {
-    /** #TALER_EXCHANGEDB_TT_DEPOSIT */
-    { "get_deposit_with_coin_pub",
-      &add_coin_deposit },
-    /** #TALER_EXCHANGEDB_TT_MELT */
-    { "get_refresh_session_by_coin",
-      &add_coin_melt },
-    /** #TALER_EXCHANGEDB_TT_PURSE_DEPOSIT */
-    { "get_purse_deposit_by_coin_pub",
-      &add_coin_purse_deposit },
-    /** #TALER_EXCHANGEDB_TT_REFUND */
-    { "get_refunds_by_coin",
-      &add_coin_refund },
-    /** #TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP */
-    { "recoup_by_old_coin",
-      &add_old_coin_recoup },
-    /** #TALER_EXCHANGEDB_TT_RECOUP */
-    { "recoup_by_coin",
-      &add_coin_recoup },
-    /** #TALER_EXCHANGEDB_TT_RECOUP_REFRESH */
-    { "recoup_by_refreshed_coin",
-      &add_coin_recoup_refresh },
-    { NULL, NULL }
-  };
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (coin_pub),
-    GNUNET_PQ_query_param_end
-  };
-  enum GNUNET_DB_QueryStatus qs;
-  struct CoinHistoryContext chc = {
-    .head = NULL,
-    .coin_pub = coin_pub,
-    .pg = pg,
-    .db_cls = cls
-  };
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Getting transactions for coin %s\n",
-              TALER_B2S (coin_pub));
-  for (unsigned int i = 0; NULL != work[i].statement; i++)
-  {
-    qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
-                                               work[i].statement,
-                                               params,
-                                               work[i].cb,
-                                               &chc);
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Coin %s yielded %d transactions of type %s\n",
-                TALER_B2S (coin_pub),
-                qs,
-                work[i].statement);
-    if ( (0 > qs) ||
-         (chc.failed) )
-    {
-      if (NULL != chc.head)
-        common_free_coin_transaction_list (cls,
-                                           chc.head);
-      *tlp = NULL;
-      if (chc.failed)
-        qs = GNUNET_DB_STATUS_HARD_ERROR;
-      return qs;
-    }
-  }
-  *tlp = chc.head;
-  if (NULL == chc.head)
-    return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
-  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
-}
-
-
 /**
  * Closure for #handle_wt_result.
  */
@@ -14927,7 +14141,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
   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->free_reserve_history = &common_free_reserve_history;
   plugin->count_known_coins = &postgres_count_known_coins;
   plugin->ensure_coin_known = &postgres_ensure_coin_known;
   plugin->get_known_coin = &postgres_get_known_coin;
@@ -14952,8 +14165,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
   plugin->insert_refresh_reveal = &postgres_insert_refresh_reveal;
   plugin->get_refresh_reveal = &postgres_get_refresh_reveal;
   plugin->get_link_data = &postgres_get_link_data;
-  plugin->get_coin_transactions = &postgres_get_coin_transactions;
-  plugin->free_coin_transaction_list = &common_free_coin_transaction_list;
   plugin->lookup_wire_transfer = &postgres_lookup_wire_transfer;
   plugin->lookup_transfer_by_deposit = &postgres_lookup_transfer_by_deposit;
   plugin->insert_aggregation_tracking = &postgres_insert_aggregation_tracking;
@@ -15133,6 +14344,12 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
   /* NEW style, sort alphabetically! */
   plugin->do_reserve_open
     = &TEH_PG_do_reserve_open;
+  plugin->free_coin_transaction_list
+    = &TEH_COMMON_free_coin_transaction_list;
+  plugin->free_reserve_history
+    = &TEH_COMMON_free_reserve_history;
+  plugin->get_coin_transactions
+    = &TEH_PG_get_coin_transactions;
   plugin->get_expired_reserves
     = &TEH_PG_get_expired_reserves;
   plugin->get_unfinished_close_requests
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index d361d539..0df32241 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -1788,6 +1788,31 @@ struct TALER_EXCHANGEDB_PurseDepositListEntry
 };
 
 
+/**
+ * Information about a /reserves/$RID/open operation in a coin transaction 
history.
+ */
+struct TALER_EXCHANGEDB_ReserveOpenListEntry
+{
+
+  /**
+   * Signature of the reserve.
+   */
+  struct TALER_ReserveSignatureP reserve_sig;
+
+  /**
+   * Contribution of the coin to the open fee, including
+   * deposit fee.
+   */
+  struct TALER_Amount coin_contribution;
+
+  /**
+   * Signature by the coin affirming the open deposit.
+   */
+  struct TALER_CoinSpendSignatureP coin_sig;
+
+};
+
+
 /**
  * Information about a /purses/$PID/deposit operation.
  */
@@ -1947,7 +1972,12 @@ enum TALER_EXCHANGEDB_TransactionType
   /**
    * Purse deposit operation.
    */
-  TALER_EXCHANGEDB_TT_PURSE_DEPOSIT = 6
+  TALER_EXCHANGEDB_TT_PURSE_DEPOSIT = 6,
+
+  /**
+   * Reserve open deposit operation.
+   */
+  TALER_EXCHANGEDB_TT_RESERVE_OPEN = 7
 
 };
 
@@ -2023,6 +2053,12 @@ struct TALER_EXCHANGEDB_TransactionList
      */
     struct TALER_EXCHANGEDB_PurseDepositListEntry *purse_deposit;
 
+    /**
+     * Coin was used to pay to open a reserve.
+     * (#TALER_EXCHANGEDB_TT_RESERVE_OPEN)
+     */
+    struct TALER_EXCHANGEDB_ReserveOpenListEntry *reserve_open;
+
   } details;
 
 };

-- 
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]