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/pg_get_reserve_history.c | 1018 +++++++++++++++++++++++++++++++ 1 file changed, 1018 insertions(+) create mode 100644 src/exchangedb/pg_get_reserve_history.c (limited to 'src/exchangedb/pg_get_reserve_history.c') 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; +} -- cgit v1.2.3