summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2022-10-15 16:19:14 +0200
committerChristian Grothoff <christian@grothoff.org>2022-10-15 16:19:14 +0200
commit38876c503ff53f7adf44bc82be4fec3ae9b02d01 (patch)
tree2727ceb2b71a86bcabb959b232d0af49c7847aa8 /src
parenteebc030f6cf61d36b80e3273709fd9f254b155c3 (diff)
downloadexchange-38876c503ff53f7adf44bc82be4fec3ae9b02d01.tar.gz
exchange-38876c503ff53f7adf44bc82be4fec3ae9b02d01.tar.bz2
exchange-38876c503ff53f7adf44bc82be4fec3ae9b02d01.zip
-refactor DB for reserve history/status routines
Diffstat (limited to 'src')
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c56
-rw-r--r--src/exchangedb/Makefile.am1
-rw-r--r--src/exchangedb/pg_get_reserve_history.c1018
-rw-r--r--src/exchangedb/pg_get_reserve_history.h67
-rw-r--r--src/exchangedb/plugin_exchangedb_common.c16
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c1033
-rw-r--r--src/include/taler_crypto_lib.h49
-rw-r--r--src/include/taler_exchangedb_plugin.h91
-rw-r--r--src/util/exchange_signatures.c94
-rw-r--r--src/util/wallet_signatures.c2
10 files changed, 1395 insertions, 1032 deletions
diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c
index 77f01de60..2cef17193 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 059c0136a..61b18d137 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 000000000..c3ccab1f2
--- /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 000000000..ee47996b2
--- /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 a0159fb86..562710eaa 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 940523b76..af441d95d 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",
@@ -4872,670 +4511,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.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
@@ -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 a861563b2..df992c6ee 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -3835,6 +3835,51 @@ TALER_exchange_online_melt_confirmation_verify (
/**
+ * 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.
*
* @param scb function to call to create the 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 a12c2ac3f..7a0d5c55e 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -1271,6 +1271,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.
*/
enum TALER_EXCHANGEDB_ReserveOperation
@@ -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 d42f70d66..8ecb3cafe 100644
--- a/src/util/exchange_signatures.c
+++ b/src/util/exchange_signatures.c
@@ -1383,6 +1383,100 @@ 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
+
+/**
+ * Response by which the exchange affirms that it has
* merged a purse into a reserve.
*/
struct TALER_PurseMergedConfirmationPS
diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c
index c57506bd8..1209bb699 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;