From 38876c503ff53f7adf44bc82be4fec3ae9b02d01 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 15 Oct 2022 16:19:14 +0200 Subject: -refactor DB for reserve history/status routines --- src/exchangedb/Makefile.am | 1 + src/exchangedb/pg_get_reserve_history.c | 1018 ++++++++++++++++++++++++++ src/exchangedb/pg_get_reserve_history.h | 67 ++ src/exchangedb/plugin_exchangedb_common.c | 16 + src/exchangedb/plugin_exchangedb_postgres.c | 1033 +-------------------------- 5 files changed, 1107 insertions(+), 1028 deletions(-) create mode 100644 src/exchangedb/pg_get_reserve_history.c create mode 100644 src/exchangedb/pg_get_reserve_history.h (limited to 'src/exchangedb') 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 + */ +/** + * @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 + */ +/** + * @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", @@ -4871,670 +4510,6 @@ postgres_do_recoup_refresh ( } -/** - * Closure for callbacks invoked via #postgres_get_reserve_history. - */ -struct ReserveHistoryContext -{ - - /** - * Which reserve are we building the history for? - */ - const struct TALER_ReservePublicKeyP *reserve_pub; - - /** - * Where we build the history. - */ - struct TALER_EXCHANGEDB_ReserveHistory *rh; - - /** - * Tail of @e rh list. - */ - struct TALER_EXCHANGEDB_ReserveHistory *rh_tail; - - /** - * Plugin context. - */ - struct PostgresClosure *pg; - - /** - * Sum of all credit transactions. - */ - struct TALER_Amount balance_in; - - /** - * Sum of all debit transactions. - */ - struct TALER_Amount balance_out; - - /** - * Set to #GNUNET_SYSERR on serious internal errors during - * the callbacks. - */ - enum GNUNET_GenericReturnValue status; -}; - - -/** - * Append and return a fresh element to the reserve - * history kept in @a rhc. - * - * @param rhc where the history is kept - * @return the fresh element that was added - */ -static struct TALER_EXCHANGEDB_ReserveHistory * -append_rh (struct ReserveHistoryContext *rhc) -{ - struct TALER_EXCHANGEDB_ReserveHistory *tail; - - tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory); - if (NULL != rhc->rh_tail) - { - rhc->rh_tail->next = tail; - rhc->rh_tail = tail; - } - else - { - rhc->rh_tail = tail; - rhc->rh = tail; - } - return tail; -} - - -/** - * Add bank transfers to result set for #postgres_get_reserve_history. - * - * @param cls a `struct ReserveHistoryContext *` - * @param result SQL result - * @param num_results number of rows in @a result - */ -static void -add_bank_to_exchange (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct ReserveHistoryContext *rhc = cls; - struct PostgresClosure *pg = rhc->pg; - - while (0 < num_results) - { - struct TALER_EXCHANGEDB_BankTransfer *bt; - struct TALER_EXCHANGEDB_ReserveHistory *tail; - - bt = GNUNET_new (struct TALER_EXCHANGEDB_BankTransfer); - { - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("wire_reference", - &bt->wire_reference), - TALER_PQ_RESULT_SPEC_AMOUNT ("credit", - &bt->amount), - GNUNET_PQ_result_spec_timestamp ("execution_date", - &bt->execution_date), - GNUNET_PQ_result_spec_string ("sender_account_details", - &bt->sender_account_details), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - --num_results)) - { - GNUNET_break (0); - GNUNET_free (bt); - rhc->status = GNUNET_SYSERR; - return; - } - } - GNUNET_assert (0 <= - TALER_amount_add (&rhc->balance_in, - &rhc->balance_in, - &bt->amount)); - bt->reserve_pub = *rhc->reserve_pub; - tail = append_rh (rhc); - tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE; - tail->details.bank = bt; - } /* end of 'while (0 < rows)' */ -} - - -/** - * Add coin withdrawals to result set for #postgres_get_reserve_history. - * - * @param cls a `struct ReserveHistoryContext *` - * @param result SQL result - * @param num_results number of rows in @a result - */ -static void -add_withdraw_coin (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct ReserveHistoryContext *rhc = cls; - struct PostgresClosure *pg = rhc->pg; - - while (0 < num_results) - { - struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc; - struct TALER_EXCHANGEDB_ReserveHistory *tail; - - cbc = GNUNET_new (struct TALER_EXCHANGEDB_CollectableBlindcoin); - { - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev", - &cbc->h_coin_envelope), - GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", - &cbc->denom_pub_hash), - TALER_PQ_result_spec_blinded_denom_sig ("denom_sig", - &cbc->sig), - GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", - &cbc->reserve_sig), - TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", - &cbc->amount_with_fee), - TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw", - &cbc->withdraw_fee), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - --num_results)) - { - GNUNET_break (0); - GNUNET_free (cbc); - rhc->status = GNUNET_SYSERR; - return; - } - } - GNUNET_assert (0 <= - TALER_amount_add (&rhc->balance_out, - &rhc->balance_out, - &cbc->amount_with_fee)); - cbc->reserve_pub = *rhc->reserve_pub; - tail = append_rh (rhc); - tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN; - tail->details.withdraw = cbc; - } -} - - -/** - * Add recoups to result set for #postgres_get_reserve_history. - * - * @param cls a `struct ReserveHistoryContext *` - * @param result SQL result - * @param num_results number of rows in @a result - */ -static void -add_recoup (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct ReserveHistoryContext *rhc = cls; - struct PostgresClosure *pg = rhc->pg; - - while (0 < num_results) - { - struct TALER_EXCHANGEDB_Recoup *recoup; - struct TALER_EXCHANGEDB_ReserveHistory *tail; - - recoup = GNUNET_new (struct TALER_EXCHANGEDB_Recoup); - { - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_AMOUNT ("amount", - &recoup->value), - GNUNET_PQ_result_spec_auto_from_type ("coin_pub", - &recoup->coin.coin_pub), - GNUNET_PQ_result_spec_auto_from_type ("coin_blind", - &recoup->coin_blind), - GNUNET_PQ_result_spec_auto_from_type ("coin_sig", - &recoup->coin_sig), - GNUNET_PQ_result_spec_timestamp ("recoup_timestamp", - &recoup->timestamp), - GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", - &recoup->coin.denom_pub_hash), - TALER_PQ_result_spec_denom_sig ( - "denom_sig", - &recoup->coin.denom_sig), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - --num_results)) - { - GNUNET_break (0); - GNUNET_free (recoup); - rhc->status = GNUNET_SYSERR; - return; - } - } - GNUNET_assert (0 <= - TALER_amount_add (&rhc->balance_in, - &rhc->balance_in, - &recoup->value)); - recoup->reserve_pub = *rhc->reserve_pub; - tail = append_rh (rhc); - tail->type = TALER_EXCHANGEDB_RO_RECOUP_COIN; - tail->details.recoup = recoup; - } /* end of 'while (0 < rows)' */ -} - - -/** - * Add exchange-to-bank transfers to result set for - * #postgres_get_reserve_history. - * - * @param cls a `struct ReserveHistoryContext *` - * @param result SQL result - * @param num_results number of rows in @a result - */ -static void -add_exchange_to_bank (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct ReserveHistoryContext *rhc = cls; - struct PostgresClosure *pg = rhc->pg; - - while (0 < num_results) - { - struct TALER_EXCHANGEDB_ClosingTransfer *closing; - struct TALER_EXCHANGEDB_ReserveHistory *tail; - - closing = GNUNET_new (struct TALER_EXCHANGEDB_ClosingTransfer); - { - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_AMOUNT ("amount", - &closing->amount), - TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee", - &closing->closing_fee), - GNUNET_PQ_result_spec_timestamp ("execution_date", - &closing->execution_date), - GNUNET_PQ_result_spec_string ("receiver_account", - &closing->receiver_account_details), - GNUNET_PQ_result_spec_auto_from_type ("wtid", - &closing->wtid), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - --num_results)) - { - GNUNET_break (0); - GNUNET_free (closing); - rhc->status = GNUNET_SYSERR; - return; - } - } - GNUNET_assert (0 <= - TALER_amount_add (&rhc->balance_out, - &rhc->balance_out, - &closing->amount)); - closing->reserve_pub = *rhc->reserve_pub; - tail = append_rh (rhc); - tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK; - tail->details.closing = closing; - } /* end of 'while (0 < rows)' */ -} - - -/** - * Add purse merge transfers to result set for - * #postgres_get_reserve_history. - * - * @param cls a `struct ReserveHistoryContext *` - * @param result SQL result - * @param num_results number of rows in @a result - */ -static void -add_p2p_merge (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct ReserveHistoryContext *rhc = cls; - struct PostgresClosure *pg = rhc->pg; - - while (0 < num_results) - { - struct TALER_EXCHANGEDB_PurseMerge *merge; - struct TALER_EXCHANGEDB_ReserveHistory *tail; - - merge = GNUNET_new (struct TALER_EXCHANGEDB_PurseMerge); - { - uint32_t flags32; - struct TALER_Amount balance; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee", - &merge->purse_fee), - TALER_PQ_RESULT_SPEC_AMOUNT ("balance", - &balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", - &merge->amount_with_fee), - GNUNET_PQ_result_spec_timestamp ("merge_timestamp", - &merge->merge_timestamp), - GNUNET_PQ_result_spec_timestamp ("purse_expiration", - &merge->purse_expiration), - GNUNET_PQ_result_spec_uint32 ("age_limit", - &merge->min_age), - GNUNET_PQ_result_spec_uint32 ("flags", - &flags32), - GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", - &merge->h_contract_terms), - GNUNET_PQ_result_spec_auto_from_type ("merge_pub", - &merge->merge_pub), - GNUNET_PQ_result_spec_auto_from_type ("purse_pub", - &merge->purse_pub), - GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", - &merge->reserve_sig), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - --num_results)) - { - GNUNET_break (0); - GNUNET_free (merge); - rhc->status = GNUNET_SYSERR; - return; - } - merge->flags = (enum TALER_WalletAccountMergeFlags) flags32; - if ( (! GNUNET_TIME_absolute_is_future ( - merge->merge_timestamp.abs_time)) && - (-1 != TALER_amount_cmp (&balance, - &merge->amount_with_fee)) ) - merge->merged = true; - } - if (merge->merged) - GNUNET_assert (0 <= - TALER_amount_add (&rhc->balance_in, - &rhc->balance_in, - &merge->amount_with_fee)); - GNUNET_assert (0 <= - TALER_amount_add (&rhc->balance_out, - &rhc->balance_out, - &merge->purse_fee)); - merge->reserve_pub = *rhc->reserve_pub; - tail = append_rh (rhc); - tail->type = TALER_EXCHANGEDB_RO_PURSE_MERGE; - tail->details.merge = merge; - } -} - - -/** - * Add paid for history requests to result set for - * #postgres_get_reserve_history. - * - * @param cls a `struct ReserveHistoryContext *` - * @param result SQL result - * @param num_results number of rows in @a result - */ -static void -add_history_requests (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct ReserveHistoryContext *rhc = cls; - struct PostgresClosure *pg = rhc->pg; - - while (0 < num_results) - { - struct TALER_EXCHANGEDB_HistoryRequest *history; - struct TALER_EXCHANGEDB_ReserveHistory *tail; - - history = GNUNET_new (struct TALER_EXCHANGEDB_HistoryRequest); - { - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee", - &history->history_fee), - GNUNET_PQ_result_spec_timestamp ("request_timestamp", - &history->request_timestamp), - GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", - &history->reserve_sig), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - --num_results)) - { - GNUNET_break (0); - GNUNET_free (history); - rhc->status = GNUNET_SYSERR; - return; - } - } - GNUNET_assert (0 <= - TALER_amount_add (&rhc->balance_out, - &rhc->balance_out, - &history->history_fee)); - history->reserve_pub = *rhc->reserve_pub; - tail = append_rh (rhc); - tail->type = TALER_EXCHANGEDB_RO_HISTORY_REQUEST; - tail->details.history = history; - } -} - - -/** - * Get all of the transaction history associated with the specified - * reserve. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param reserve_pub public key of the reserve - * @param[out] balance set to the reserve balance - * @param[out] rhp set to known transaction history (NULL if reserve is unknown) - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_get_reserve_history (void *cls, - const struct TALER_ReservePublicKeyP *reserve_pub, - struct TALER_Amount *balance, - struct TALER_EXCHANGEDB_ReserveHistory **rhp) -{ - struct PostgresClosure *pg = cls; - struct ReserveHistoryContext rhc; - struct - { - /** - * Name of the prepared statement to run. - */ - const char *statement; - /** - * Function to use to process the results. - */ - GNUNET_PQ_PostgresResultHandler cb; - } work[] = { - /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */ - { "reserves_in_get_transactions", - add_bank_to_exchange }, - /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */ - { "get_reserves_out", - &add_withdraw_coin }, - /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */ - { "recoup_by_reserve", - &add_recoup }, - /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */ - { "close_by_reserve", - &add_exchange_to_bank }, - /** #TALER_EXCHANGEDB_RO_PURSE_MERGE */ - { "merge_by_reserve", - &add_p2p_merge }, - /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */ - { "history_by_reserve", - &add_history_requests }, - /* List terminator */ - { NULL, - NULL } - }; - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (reserve_pub), - GNUNET_PQ_query_param_end - }; - - rhc.reserve_pub = reserve_pub; - rhc.rh = NULL; - rhc.rh_tail = NULL; - rhc.pg = pg; - rhc.status = GNUNET_OK; - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (pg->currency, - &rhc.balance_in)); - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (pg->currency, - &rhc.balance_out)); - qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */ - for (unsigned int i = 0; NULL != work[i].cb; i++) - { - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - work[i].statement, - params, - work[i].cb, - &rhc); - if ( (0 > qs) || - (GNUNET_OK != rhc.status) ) - break; - } - if ( (qs < 0) || - (rhc.status != GNUNET_OK) ) - { - TEH_COMMON_free_reserve_history (cls, - rhc.rh); - rhc.rh = NULL; - if (qs >= 0) - { - /* status == SYSERR is a very hard error... */ - qs = GNUNET_DB_STATUS_HARD_ERROR; - } - } - *rhp = rhc.rh; - GNUNET_assert (0 <= - TALER_amount_subtract (balance, - &rhc.balance_in, - &rhc.balance_out)); - return qs; -} - - -/** - * Get a truncated transaction history associated with the specified - * reserve. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param reserve_pub public key of the reserve - * @param[out] balance_in set to the total of inbound - * transactions in the returned history - * @param[out] balance_out set to the total of outbound - * transactions in the returned history - * @param[out] rhp set to known transaction history (NULL if reserve is unknown) - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_get_reserve_status (void *cls, - const struct TALER_ReservePublicKeyP *reserve_pub, - struct TALER_Amount *balance_in, - struct TALER_Amount *balance_out, - struct TALER_EXCHANGEDB_ReserveHistory **rhp) -{ - struct PostgresClosure *pg = cls; - struct ReserveHistoryContext rhc; - struct - { - /** - * Name of the prepared statement to run. - */ - const char *statement; - /** - * Function to use to process the results. - */ - GNUNET_PQ_PostgresResultHandler cb; - } work[] = { - /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */ - { "reserves_in_get_transactions_truncated", - add_bank_to_exchange }, - /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */ - { "get_reserves_out_truncated", - &add_withdraw_coin }, - /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */ - { "recoup_by_reserve_truncated", - &add_recoup }, - /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */ - { "close_by_reserve_truncated", - &add_exchange_to_bank }, - /** #TALER_EXCHANGEDB_RO_PURSE_MERGE */ - { "merge_by_reserve_truncated", - &add_p2p_merge }, - /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */ - { "history_by_reserve_truncated", - &add_history_requests }, - /* List terminator */ - { NULL, - NULL } - }; - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_TIME_Absolute timelimit; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (reserve_pub), - GNUNET_PQ_query_param_absolute_time (&timelimit), - GNUNET_PQ_query_param_end - }; - - timelimit = GNUNET_TIME_absolute_subtract ( - GNUNET_TIME_absolute_get (), - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS, - 5)); - rhc.reserve_pub = reserve_pub; - rhc.rh = NULL; - rhc.rh_tail = NULL; - rhc.pg = pg; - rhc.status = GNUNET_OK; - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (pg->currency, - &rhc.balance_in)); - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (pg->currency, - &rhc.balance_out)); - qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */ - for (unsigned int i = 0; NULL != work[i].cb; i++) - { - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - work[i].statement, - params, - work[i].cb, - &rhc); - if ( (0 > qs) || - (GNUNET_OK != rhc.status) ) - break; - } - if ( (qs < 0) || - (rhc.status != GNUNET_OK) ) - { - TEH_COMMON_free_reserve_history (cls, - rhc.rh); - rhc.rh = NULL; - if (qs >= 0) - { - /* status == SYSERR is a very hard error... */ - qs = GNUNET_DB_STATUS_HARD_ERROR; - } - } - *rhp = rhc.rh; - *balance_in = rhc.balance_in; - *balance_out = rhc.balance_out; - return qs; -} - - /** * Get the balance of the specified reserve. * @@ -14139,8 +13114,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) plugin->do_recoup = &postgres_do_recoup; plugin->do_recoup_refresh = &postgres_do_recoup_refresh; plugin->get_reserve_balance = &postgres_get_reserve_balance; - plugin->get_reserve_history = &postgres_get_reserve_history; - plugin->get_reserve_status = &postgres_get_reserve_status; plugin->count_known_coins = &postgres_count_known_coins; plugin->ensure_coin_known = &postgres_ensure_coin_known; plugin->get_known_coin = &postgres_get_known_coin; @@ -14352,6 +13325,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_get_coin_transactions; plugin->get_expired_reserves = &TEH_PG_get_expired_reserves; + plugin->get_reserve_history + = &TEH_PG_get_reserve_history; + plugin->get_reserve_status + = &TEH_PG_get_reserve_status; plugin->get_unfinished_close_requests = &TEH_PG_get_unfinished_close_requests; plugin->insert_records_by_table -- cgit v1.2.3