diff options
author | Christian Grothoff <christian@grothoff.org> | 2022-10-13 19:07:25 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2022-10-13 19:07:25 +0200 |
commit | 09310cc66ebbdf083c4b4fa86a368b3ea52c0c16 (patch) | |
tree | b87132659d9d27e5e7d84e4f76b4d836b6689f9d | |
parent | 4fc77b9dbfee88dff2d366d7dbb2d91797f8b9a0 (diff) |
-implement reserve closure in test
-rw-r--r-- | src/exchange/taler-exchange-closer.c | 19 | ||||
-rw-r--r-- | src/exchangedb/Makefile.am | 2 | ||||
-rw-r--r-- | src/exchangedb/bench_db.c | 18 | ||||
-rw-r--r-- | src/exchangedb/common-0001.sql | 92 | ||||
-rw-r--r-- | src/exchangedb/exchange-0001-part.sql | 19 | ||||
-rw-r--r-- | src/exchangedb/pg_get_expired_reserves.c | 173 | ||||
-rw-r--r-- | src/exchangedb/pg_get_expired_reserves.h | 45 | ||||
-rw-r--r-- | src/exchangedb/pg_get_unfinished_close_requests.c | 162 | ||||
-rw-r--r-- | src/exchangedb/pg_get_unfinished_close_requests.h | 46 | ||||
-rw-r--r-- | src/exchangedb/pg_insert_records_by_table.c | 52 | ||||
-rw-r--r-- | src/exchangedb/pg_lookup_records_by_table.c | 9 | ||||
-rw-r--r-- | src/exchangedb/pg_lookup_serial_by_table.c | 8 | ||||
-rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 165 | ||||
-rw-r--r-- | src/exchangedb/shard-0001-part.sql | 5 | ||||
-rw-r--r-- | src/include/taler_exchangedb_plugin.h | 29 | ||||
-rw-r--r-- | src/testing/test_exchange_p2p.c | 3 |
16 files changed, 536 insertions, 311 deletions
diff --git a/src/exchange/taler-exchange-closer.c b/src/exchange/taler-exchange-closer.c index 0e203f0fe..eacfa5d50 100644 --- a/src/exchange/taler-exchange-closer.c +++ b/src/exchange/taler-exchange-closer.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016-2021 Taler Systems SA + Copyright (C) 2016-2022 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -431,11 +431,18 @@ run_reserve_closures (void *cls) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Checking for reserves to close by date %s\n", GNUNET_TIME_timestamp2s (now)); - qs = db_plugin->get_expired_reserves (db_plugin->cls, - now, - &expired_reserve_cb, - NULL); - GNUNET_assert (1 >= qs); + qs = db_plugin->get_unfinished_close_requests (db_plugin->cls, + &expired_reserve_cb, + NULL); + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + /* Try expired reserves as well */ + qs = db_plugin->get_expired_reserves ( + db_plugin->cls, + now, + &expired_reserve_cb, + NULL); + } switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am index 74edfc4c5..e4094cd71 100644 --- a/src/exchangedb/Makefile.am +++ b/src/exchangedb/Makefile.am @@ -70,6 +70,8 @@ endif libtaler_plugin_exchangedb_postgres_la_SOURCES = \ plugin_exchangedb_postgres.c pg_helper.h \ pg_do_reserve_open.c pg_do_reserve_open.h \ + pg_get_expired_reserves.c pg_get_expired_reserves.h \ + pg_get_unfinished_close_requests.c pg_get_unfinished_close_requests.h \ pg_insert_close_request.c pg_insert_close_request.h \ pg_insert_records_by_table.c pg_insert_records_by_table.h \ pg_insert_reserve_open_deposit.c pg_insert_reserve_open_deposit.h \ diff --git a/src/exchangedb/bench_db.c b/src/exchangedb/bench_db.c index a8dbfbfa5..a85834d13 100644 --- a/src/exchangedb/bench_db.c +++ b/src/exchangedb/bench_db.c @@ -51,32 +51,28 @@ prepare (struct GNUNET_PQ_Context *conn) "(hc" ",expiration_date" ") VALUES " - "($1, $2);", - 2), + "($1, $2);"), /* Used in #postgres_iterate_denomination_info() */ GNUNET_PQ_make_prepare ( "bm_select", "SELECT" " expiration_date" " FROM benchmap" - " WHERE hc=$1;", - 1), + " WHERE hc=$1;"), GNUNET_PQ_make_prepare ( "bhm_insert", "INSERT INTO benchhmap " "(hc" ",expiration_date" ") VALUES " - "($1, $2);", - 2), + "($1, $2);"), /* Used in #postgres_iterate_denomination_info() */ GNUNET_PQ_make_prepare ( "bhm_select", "SELECT" " expiration_date" " FROM benchhmap" - " WHERE hc=$1;", - 1), + " WHERE hc=$1;"), GNUNET_PQ_make_prepare ( "bem_insert", "INSERT INTO benchemap " @@ -84,16 +80,14 @@ prepare (struct GNUNET_PQ_Context *conn) ",ihc" ",expiration_date" ") VALUES " - "($1, $2, $3);", - 3), + "($1, $2, $3);"), /* Used in #postgres_iterate_denomination_info() */ GNUNET_PQ_make_prepare ( "bem_select", "SELECT" " expiration_date" " FROM benchemap" - " WHERE ihc=$1 AND hc=$2;", - 2), + " WHERE ihc=$1 AND hc=$2;"), GNUNET_PQ_PREPARED_STATEMENT_END }; enum GNUNET_GenericReturnValue ret; diff --git a/src/exchangedb/common-0001.sql b/src/exchangedb/common-0001.sql index 68d8643ed..9f32ede74 100644 --- a/src/exchangedb/common-0001.sql +++ b/src/exchangedb/common-0001.sql @@ -502,57 +502,6 @@ END $$; ---------------------------- reserves_close_requests ------------------------------- - -CREATE OR REPLACE FUNCTION create_table_reserves_close_requests( - IN shard_suffix VARCHAR DEFAULT NULL -) -RETURNS VOID -LANGUAGE plpgsql -AS $$ -DECLARE - table_name VARCHAR default 'reserves_close_requests'; -BEGIN - - PERFORM create_partitioned_table( - 'CREATE TABLE IF NOT EXISTS %I' - '(close_request_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE / PRIMARY KEY' - ',reserve_pub BYTEA NOT NULL' -- REFERENCES reserves (reserve_pub) ON DELETE CASCADE' - ',execution_date INT8 NOT NULL' - ',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)' - ',wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)' - ') %s ;' - ,table_name - ,'PARTITION BY HASH (reserve_pub)' - ,shard_suffix - ); - - table_name = concat_ws('_', table_name, shard_suffix); - - EXECUTE FORMAT ( - 'CREATE INDEX IF NOT EXISTS ' || table_name || '_by_close_request_uuid_index ' - 'ON ' || table_name || ' ' - '(close_request_uuid);' - ); -END -$$; - -CREATE OR REPLACE FUNCTION add_constraints_to_reserves_close_requests_partition( - IN partition_suffix VARCHAR -) -RETURNS void -LANGUAGE plpgsql -AS $$ -BEGIN - EXECUTE FORMAT ( - 'ALTER TABLE reserves_close_requests_' || partition_suffix || ' ' - 'ADD CONSTRAINT reserves_close_' || partition_suffix || '_close_request_uuid_pkey ' - 'PRIMARY KEY (close_request_uuid)' - ); -END -$$; - - ---------------------------- reserves_out ------------------------------- CREATE OR REPLACE FUNCTION create_table_reserves_out( @@ -1752,16 +1701,57 @@ BEGIN ',close_fee_val INT8 NOT NULL' ',close_fee_frac INT4 NOT NULL' ',payto_uri VARCHAR NOT NULL' + ',done BOOL NOT NULL DEFAULT(FALSE)' ',PRIMARY KEY (reserve_pub,close_timestamp)' ') %s ;' ,table_name ,'PARTITION BY HASH (reserve_pub)' ,shard_suffix ); +END +$$; + +CREATE OR REPLACE FUNCTION add_constraints_to_close_requests( + IN partition_suffix VARCHAR +) +RETURNS VOID +LANGUAGE plpgsql +AS $$ +DECLARE + table_name VARCHAR DEFAULT 'close_requests'; +BEGIN + + EXECUTE FORMAT ( + 'CREATE INDEX IF NOT EXISTS ' || table_name || '_by_close_request_uuid_index ' + 'ON ' || table_name || ' ' + '(close_request_serial_id);' + ); + EXECUTE FORMAT ( + 'CREATE INDEX IF NOT EXISTS ' || table_name || '_by_close_request_done_index ' + 'ON ' || table_name || ' ' + '(done);' + ); +END +$$; + +CREATE OR REPLACE FUNCTION add_constraints_to_close_requests_partition( + IN partition_suffix VARCHAR +) +RETURNS void +LANGUAGE plpgsql +AS $$ +BEGIN + EXECUTE FORMAT ( + 'ALTER TABLE close_requests_' || partition_suffix || ' ' + 'ADD CONSTRAINT close_requests_' || partition_suffix || '_close_request_uuid_pkey ' + 'UNIQUE (close_request_serial_id)' + ); END $$; + + ------------------------------- purse_deposits ------------------------------- CREATE OR REPLACE FUNCTION create_table_purse_deposits( diff --git a/src/exchangedb/exchange-0001-part.sql b/src/exchangedb/exchange-0001-part.sql index 48515a478..760acd98b 100644 --- a/src/exchangedb/exchange-0001-part.sql +++ b/src/exchangedb/exchange-0001-part.sql @@ -269,22 +269,6 @@ CREATE TABLE IF NOT EXISTS reserves_open_deposits_default SELECT add_constraints_to_reserves_open_deposits_partition('default'); --- ------------------------------ reserves_close_requests ---------------------------------------- - -SELECT create_table_reserves_close_requests(); - -COMMENT ON TABLE reserves_close_requests - IS 'explicit requests by clients to affect an immediate closure of a reserve'; -COMMENT ON COLUMN reserves_close_requests.wire_target_h_payto - IS 'Identifies the credited bank account. Optional.'; - -CREATE TABLE IF NOT EXISTS reserves_close_requests_default - PARTITION OF reserves_close_requests - FOR VALUES WITH (MODULUS 1, REMAINDER 0); - -SELECT add_constraints_to_reserves_close_requests_partition('default'); - - -- ------------------------------ reserves_out ---------------------------------------- SELECT create_table_reserves_out(); @@ -1284,11 +1268,14 @@ COMMENT ON COLUMN close_requests.reserve_sig IS 'Signature affirming that the reserve is to be closed'; COMMENT ON COLUMN close_requests.close_val IS 'Balance of the reserve at the time of closing, to be wired to the associated bank account (minus the closing fee)'; +COMMENT ON COLUMN close_requests.payto_uri + IS 'Identifies the credited bank account. Optional.'; CREATE TABLE IF NOT EXISTS close_requests_default PARTITION OF close_requests FOR VALUES WITH (MODULUS 1, REMAINDER 0); +SELECT add_constraints_to_close_requests_partition('default'); -- ------------------------------ purse_deposits ---------------------------------------- diff --git a/src/exchangedb/pg_get_expired_reserves.c b/src/exchangedb/pg_get_expired_reserves.c new file mode 100644 index 000000000..07a739115 --- /dev/null +++ b/src/exchangedb/pg_get_expired_reserves.c @@ -0,0 +1,173 @@ +/* + 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_expired_reserves.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_expired_reserves.h" +#include "pg_helper.h" + + +/** + * Closure for #reserve_expired_cb(). + */ +struct ExpiredReserveContext +{ + /** + * Function to call for each expired reserve. + */ + TALER_EXCHANGEDB_ReserveExpiredCallback rec; + + /** + * Closure to give to @e rec. + */ + void *rec_cls; + + /** + * Plugin context. + */ + struct PostgresClosure *pg; + + /** + * Set to #GNUNET_SYSERR on error. + */ + enum GNUNET_GenericReturnValue status; +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls closure + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +reserve_expired_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct ExpiredReserveContext *erc = cls; + struct PostgresClosure *pg = erc->pg; + enum GNUNET_GenericReturnValue ret; + + ret = GNUNET_OK; + for (unsigned int i = 0; i<num_results; i++) + { + struct GNUNET_TIME_Timestamp exp_date; + char *account_details; + struct TALER_ReservePublicKeyP reserve_pub; + struct TALER_Amount remaining_balance; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_timestamp ("expiration_date", + &exp_date), + GNUNET_PQ_result_spec_string ("account_details", + &account_details), + GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", + &reserve_pub), + TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance", + &remaining_balance), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + ret = GNUNET_SYSERR; + break; + } + ret = erc->rec (erc->rec_cls, + &reserve_pub, + &remaining_balance, + account_details, + exp_date); + GNUNET_PQ_cleanup_result (rs); + if (GNUNET_OK != ret) + break; + } + erc->status = ret; +} + + +enum GNUNET_DB_QueryStatus +TEH_PG_get_expired_reserves (void *cls, + struct GNUNET_TIME_Timestamp now, + TALER_EXCHANGEDB_ReserveExpiredCallback rec, + void *rec_cls) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_timestamp (&now), + GNUNET_PQ_query_param_end + }; + struct ExpiredReserveContext ectx = { + .rec = rec, + .rec_cls = rec_cls, + .pg = pg, + .status = GNUNET_OK + }; + enum GNUNET_DB_QueryStatus qs; + + PREPARE (pg, + "get_expired_reserves", + "WITH ed AS MATERIALIZED ( " + " SELECT * " + " FROM reserves " + " WHERE expiration_date <= $1 " + " AND (current_balance_val != 0 OR current_balance_frac != 0) " + " ORDER BY expiration_date ASC " + " LIMIT 1 " + ") " + "SELECT " + " ed.expiration_date " + " ,payto_uri AS account_details " + " ,ed.reserve_pub " + " ,current_balance_val " + " ,current_balance_frac " + "FROM ( " + " SELECT " + " * " + " FROM reserves_in " + " WHERE reserve_pub = ( " + " SELECT reserve_pub FROM ed) " + " ) ri " + "JOIN wire_targets wt ON (ri.wire_source_h_payto = wt.wire_target_h_payto) " + "JOIN ed ON (ri.reserve_pub = ed.reserve_pub);"); + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "get_expired_reserves", + params, + &reserve_expired_cb, + &ectx); + switch (ectx.status) + { + case GNUNET_SYSERR: + return GNUNET_DB_STATUS_HARD_ERROR; + case GNUNET_NO: + return GNUNET_DB_STATUS_SOFT_ERROR; + case GNUNET_OK: + break; + } + return qs; +} diff --git a/src/exchangedb/pg_get_expired_reserves.h b/src/exchangedb/pg_get_expired_reserves.h new file mode 100644 index 000000000..0874b531a --- /dev/null +++ b/src/exchangedb/pg_get_expired_reserves.h @@ -0,0 +1,45 @@ +/* + 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_expired_reserves.h + * @brief implementation of the get_expired_reserves function + * @author Christian Grothoff + */ +#ifndef PG_GET_EXPIRED_RESERVES_H +#define PG_GET_EXPIRED_RESERVES_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" + + +/** + * Obtain information about expired reserves and their + * remaining balances. + * + * @param cls closure of the plugin + * @param now timestamp based on which we decide expiration + * @param rec function to call on expired reserves + * @param rec_cls closure for @a rec + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +TEH_PG_get_expired_reserves (void *cls, + struct GNUNET_TIME_Timestamp now, + TALER_EXCHANGEDB_ReserveExpiredCallback rec, + void *rec_cls); + +#endif diff --git a/src/exchangedb/pg_get_unfinished_close_requests.c b/src/exchangedb/pg_get_unfinished_close_requests.c new file mode 100644 index 000000000..d9da6a7c0 --- /dev/null +++ b/src/exchangedb/pg_get_unfinished_close_requests.c @@ -0,0 +1,162 @@ +/* + 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_unfinished_close_requests.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_unfinished_close_requests.h" +#include "pg_helper.h" + + +/** + * Closure for #reserve_close_cb(). + */ +struct CloseReserveContext +{ + /** + * Function to call for each to be closed reserve. + */ + TALER_EXCHANGEDB_ReserveExpiredCallback rec; + + /** + * Closure to give to @e rec. + */ + void *rec_cls; + + /** + * Plugin context. + */ + struct PostgresClosure *pg; + + /** + * Set to #GNUNET_SYSERR on error. + */ + enum GNUNET_GenericReturnValue status; +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls closure + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +reserve_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct CloseReserveContext *erc = cls; + struct PostgresClosure *pg = erc->pg; + enum GNUNET_GenericReturnValue ret; + + ret = GNUNET_OK; + for (unsigned int i = 0; i<num_results; i++) + { + struct GNUNET_TIME_Timestamp exp_date; + char *account_details; + struct TALER_ReservePublicKeyP reserve_pub; + struct TALER_Amount remaining_balance; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_timestamp ("expiration_date", + &exp_date), + GNUNET_PQ_result_spec_string ("account_details", + &account_details), + GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", + &reserve_pub), + TALER_PQ_RESULT_SPEC_AMOUNT ("close", + &remaining_balance), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + ret = GNUNET_SYSERR; + break; + } + ret = erc->rec (erc->rec_cls, + &reserve_pub, + &remaining_balance, + account_details, + exp_date); + GNUNET_PQ_cleanup_result (rs); + if (GNUNET_OK != ret) + break; + } + erc->status = ret; +} + + +enum GNUNET_DB_QueryStatus +TEH_PG_get_unfinished_close_requests ( + void *cls, + TALER_EXCHANGEDB_ReserveExpiredCallback rec, + void *rec_cls) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_end + }; + struct CloseReserveContext ectx = { + .rec = rec, + .rec_cls = rec_cls, + .pg = pg, + .status = GNUNET_OK + }; + enum GNUNET_DB_QueryStatus qs; + + PREPARE (pg, + "get_unfinished_close_requests", + "UPDATE close_requests AS rc" + " SET done=TRUE" + " WHERE done=FALSE" + " RETURNING" + " reserve_pub" + " ,close_timestamp AS expiration_date" + " ,close_val" + " ,close_frac" + " ,(SELECT payto_uri" + " FROM reserves_in ri" + " JOIN wire_targets wt ON (ri.wire_source_h_payto = wt.wire_target_h_payto)" + " WHERE ri.reserve_pub=rc.reserve_pub)" + " AS account_details;"); + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "get_unfinished_close_requests", + params, + &reserve_cb, + &ectx); + switch (ectx.status) + { + case GNUNET_SYSERR: + return GNUNET_DB_STATUS_HARD_ERROR; + case GNUNET_NO: + return GNUNET_DB_STATUS_SOFT_ERROR; + case GNUNET_OK: + break; + } + return qs; +} diff --git a/src/exchangedb/pg_get_unfinished_close_requests.h b/src/exchangedb/pg_get_unfinished_close_requests.h new file mode 100644 index 000000000..4c5aa0d1d --- /dev/null +++ b/src/exchangedb/pg_get_unfinished_close_requests.h @@ -0,0 +1,46 @@ +/* + 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_unfinished_close_requests.h + * @brief implementation of the get_unfinished_close_requests function + * @author Christian Grothoff + */ +#ifndef PG_GET_UNFINISHED_CLOSE_REQUESTS_H +#define PG_GET_UNFINISHED_CLOSE_REQUESTS_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" + + +/** + * Obtain information about force-closed reserves + * where the close was not yet done (and their remaining + * balances). Updates the returned reserve's close + * status to "done". + * + * @param cls closure of the plugin + * @param rec function to call on expired reserves + * @param rec_cls closure for @a rec + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +TEH_PG_get_unfinished_close_requests ( + void *cls, + TALER_EXCHANGEDB_ReserveExpiredCallback rec, + void *rec_cls); + +#endif diff --git a/src/exchangedb/pg_insert_records_by_table.c b/src/exchangedb/pg_insert_records_by_table.c index 90d389873..5613166cd 100644 --- a/src/exchangedb/pg_insert_records_by_table.c +++ b/src/exchangedb/pg_insert_records_by_table.c @@ -436,46 +436,6 @@ irbt_cb_table_reserves_open_deposits ( * @param td record to insert */ static enum GNUNET_DB_QueryStatus -irbt_cb_table_reserves_close_requests ( - struct PostgresClosure *pg, - const struct TALER_EXCHANGEDB_TableData *td) -{ - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&td->serial), - GNUNET_PQ_query_param_auto_from_type ( - &td->details.reserves_close_requests.reserve_pub), - GNUNET_PQ_query_param_timestamp ( - &td->details.reserves_close_requests.execution_date), - GNUNET_PQ_query_param_auto_from_type ( - &td->details.reserves_close_requests.reserve_sig), - GNUNET_PQ_query_param_auto_from_type ( - &td->details.reserves_close_requests.wire_target_h_payto), - GNUNET_PQ_query_param_end - }; - - PREPARE (pg, - "insert_into_table_reserves_close_requests", - "INSERT INTO reserves_close_requests" - "(close_request_uuid" - ",reserve_pub" - ",execution_date" - ",reserve_sig" - ",wire_target_h_payto" - ") VALUES " - "($1, $2, $3, $4, $5);"); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_into_table_reserves_close_requests", - params); -} - - -/** - * Function called with reserves_close records to insert into table. - * - * @param pg plugin context - * @param td record to insert - */ -static enum GNUNET_DB_QueryStatus irbt_cb_table_reserves_close (struct PostgresClosure *pg, const struct TALER_EXCHANGEDB_TableData *td) { @@ -1582,6 +1542,10 @@ irbt_cb_table_close_requests (struct PostgresClosure *pg, &td->details.close_requests.reserve_sig), TALER_PQ_query_param_amount ( &td->details.close_requests.close), + TALER_PQ_query_param_amount ( + &td->details.close_requests.close_fee), + GNUNET_PQ_query_param_string ( + td->details.close_requests.payto_uri), GNUNET_PQ_query_param_end }; @@ -1594,8 +1558,11 @@ irbt_cb_table_close_requests (struct PostgresClosure *pg, ",reserve_sig" ",close_val" ",close_frac" + ",close_fee_val" + ",close_fee_frac" + ",payto_uri" ") VALUES " - "($1, $2, $3, $4, $5, $6);"); + "($1, $2, $3, $4, $5, $6, $7, $8, $9);"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_into_table_close_requests", params); @@ -1883,9 +1850,6 @@ TEH_PG_insert_records_by_table (void *cls, case TALER_EXCHANGEDB_RT_RESERVES_OPEN_DEPOSITS: rh = &irbt_cb_table_reserves_open_deposits; break; - case TALER_EXCHANGEDB_RT_RESERVES_CLOSE_REQUESTS: - rh = &irbt_cb_table_reserves_close_requests; - break; case TALER_EXCHANGEDB_RT_RESERVES_OUT: rh = &irbt_cb_table_reserves_out; break; diff --git a/src/exchangedb/pg_lookup_records_by_table.c b/src/exchangedb/pg_lookup_records_by_table.c index 9e47de481..dc1f17caa 100644 --- a/src/exchangedb/pg_lookup_records_by_table.c +++ b/src/exchangedb/pg_lookup_records_by_table.c @@ -1867,12 +1867,21 @@ lrbt_cb_table_close_requests (void *cls, GNUNET_PQ_result_spec_auto_from_type ( "reserve_pub", &td.details.close_requests.reserve_pub), + GNUNET_PQ_result_spec_timestamp ( + "close_timestamp", + &td.details.close_requests.close_timestamp), GNUNET_PQ_result_spec_auto_from_type ( "reserve_sig", &td.details.close_requests.reserve_sig), TALER_PQ_RESULT_SPEC_AMOUNT ( "close", &td.details.close_requests.close), + TALER_PQ_RESULT_SPEC_AMOUNT ( + "close_fee", + &td.details.close_requests.close_fee), + GNUNET_PQ_result_spec_string ( + "payto_uri", + &td.details.close_requests.payto_uri), GNUNET_PQ_result_spec_end }; diff --git a/src/exchangedb/pg_lookup_serial_by_table.c b/src/exchangedb/pg_lookup_serial_by_table.c index 500569c14..8dc6e061a 100644 --- a/src/exchangedb/pg_lookup_serial_by_table.c +++ b/src/exchangedb/pg_lookup_serial_by_table.c @@ -133,14 +133,6 @@ TEH_PG_lookup_serial_by_table (void *cls, " ORDER BY open_request_uuid DESC" " LIMIT 1;"); break; - case TALER_EXCHANGEDB_RT_RESERVES_CLOSE_REQUESTS: - XPREPARE ("select_serial_by_table_reserves_close_requests", - "SELECT" - " close_request_uuid AS serial" - " FROM reserves_close_requests" - " ORDER BY close_request_uuid DESC" - " LIMIT 1;"); - break; case TALER_EXCHANGEDB_RT_RESERVES_OUT: XPREPARE ("select_serial_by_table_reserves_out", "SELECT" diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 899980471..9bf421553 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -31,6 +31,8 @@ #include "taler_exchangedb_plugin.h" #include "pg_helper.h" #include "pg_do_reserve_open.h" +#include "pg_get_expired_reserves.h" +#include "pg_get_unfinished_close_requests.h" #include "pg_insert_close_request.h" #include "pg_insert_records_by_table.h" #include "pg_insert_reserve_open_deposit.h" @@ -2344,32 +2346,6 @@ prepare_statements (struct PostgresClosure *pg) " FROM history_requests" " WHERE reserve_pub=$1" " AND request_timestamp>=$2;"), - /* Used in #postgres_get_expired_reserves() */ - GNUNET_PQ_make_prepare ( - "get_expired_reserves", - "WITH ed AS MATERIALIZED ( " - " SELECT * " - " FROM reserves " - " WHERE expiration_date <= $1 " - " AND (current_balance_val != 0 OR current_balance_frac != 0) " - " ORDER BY expiration_date ASC " - " LIMIT 1 " - ") " - "SELECT " - " ed.expiration_date " - " ,payto_uri AS account_details " - " ,ed.reserve_pub " - " ,current_balance_val " - " ,current_balance_frac " - "FROM ( " - " SELECT " - " * " - " FROM reserves_in " - " WHERE reserve_pub = ( " - " SELECT reserve_pub FROM ed) " - " ) ri " - "JOIN wire_targets wt ON (ri.wire_source_h_payto = wt.wire_target_h_payto) " - "JOIN ed ON (ri.reserve_pub = ed.reserve_pub);"), /* Used in #postgres_get_coin_transactions() to obtain recoup transactions for a coin */ GNUNET_PQ_make_prepare ( @@ -8551,138 +8527,6 @@ postgres_insert_global_fee (void *cls, /** - * Closure for #reserve_expired_cb(). - */ -struct ExpiredReserveContext -{ - /** - * Function to call for each expired reserve. - */ - TALER_EXCHANGEDB_ReserveExpiredCallback rec; - - /** - * Closure to give to @e rec. - */ - void *rec_cls; - - /** - * Plugin context. - */ - struct PostgresClosure *pg; - - /** - * Set to #GNUNET_SYSERR on error. - */ - enum GNUNET_GenericReturnValue status; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results. - * - * @param cls closure - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -reserve_expired_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct ExpiredReserveContext *erc = cls; - struct PostgresClosure *pg = erc->pg; - enum GNUNET_GenericReturnValue ret; - - ret = GNUNET_OK; - for (unsigned int i = 0; i<num_results; i++) - { - struct GNUNET_TIME_Timestamp exp_date; - char *account_details; - struct TALER_ReservePublicKeyP reserve_pub; - struct TALER_Amount remaining_balance; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_timestamp ("expiration_date", - &exp_date), - GNUNET_PQ_result_spec_string ("account_details", - &account_details), - GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", - &reserve_pub), - TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance", - &remaining_balance), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ret = GNUNET_SYSERR; - break; - } - ret = erc->rec (erc->rec_cls, - &reserve_pub, - &remaining_balance, - account_details, - exp_date); - GNUNET_PQ_cleanup_result (rs); - if (GNUNET_OK != ret) - break; - } - erc->status = ret; -} - - -/** - * Obtain information about expired reserves and their - * remaining balances. - * - * @param cls closure of the plugin - * @param now timestamp based on which we decide expiration - * @param rec function to call on expired reserves - * @param rec_cls closure for @a rec - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_get_expired_reserves (void *cls, - struct GNUNET_TIME_Timestamp now, - TALER_EXCHANGEDB_ReserveExpiredCallback rec, - void *rec_cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_timestamp (&now), - GNUNET_PQ_query_param_end - }; - struct ExpiredReserveContext ectx = { - .rec = rec, - .rec_cls = rec_cls, - .pg = pg, - .status = GNUNET_OK - }; - enum GNUNET_DB_QueryStatus qs; - - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "get_expired_reserves", - params, - &reserve_expired_cb, - &ectx); - switch (ectx.status) - { - case GNUNET_SYSERR: - return GNUNET_DB_STATUS_HARD_ERROR; - case GNUNET_NO: - return GNUNET_DB_STATUS_SOFT_ERROR; - case GNUNET_OK: - break; - } - return qs; -} - - -/** * Insert reserve close operation into database. * * @param cls closure @@ -15118,7 +14962,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) plugin->get_wire_fee = &postgres_get_wire_fee; plugin->get_global_fee = &postgres_get_global_fee; plugin->get_global_fees = &postgres_get_global_fees; - plugin->get_expired_reserves = &postgres_get_expired_reserves; plugin->insert_reserve_closed = &postgres_insert_reserve_closed; plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert; plugin->wire_prepare_data_mark_finished = @@ -15290,6 +15133,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) /* NEW style, sort alphabetically! */ plugin->do_reserve_open = &TEH_PG_do_reserve_open; + plugin->get_expired_reserves + = &TEH_PG_get_expired_reserves; + plugin->get_unfinished_close_requests + = &TEH_PG_get_unfinished_close_requests; plugin->insert_records_by_table = &TEH_PG_insert_records_by_table; plugin->insert_reserve_open_deposit diff --git a/src/exchangedb/shard-0001-part.sql b/src/exchangedb/shard-0001-part.sql index 0f20be63e..a54eb8dc8 100644 --- a/src/exchangedb/shard-0001-part.sql +++ b/src/exchangedb/shard-0001-part.sql @@ -50,9 +50,6 @@ BEGIN PERFORM create_table_reserves_open_deposits(shard_suffix); PERFORM add_constraints_to_reserves_open_deposits_partition(shard_suffix); - PERFORM create_table_reserves_close_requests(shard_suffix); - PERFORM add_constraints_to_reserves_close_requests_partition(shard_suffix); - PERFORM create_table_reserves_out(shard_suffix); PERFORM add_constraints_to_reserves_out_partition(shard_suffix); @@ -119,6 +116,8 @@ BEGIN PERFORM create_table_history_requests(shard_suffix); PERFORM create_table_close_requests(shard_suffix); + PERFORM add_constraints_to_close_requests_partition(shard_suffix); + PERFORM create_table_purse_deposits(shard_suffix); PERFORM add_constraints_to_purse_deposits_partition(shard_suffix); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 06fa2479d..d361d5393 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -202,7 +202,6 @@ enum TALER_EXCHANGEDB_ReplicatedTable TALER_EXCHANGEDB_RT_RESERVES_CLOSE, TALER_EXCHANGEDB_RT_RESERVES_OPEN_REQUESTS, TALER_EXCHANGEDB_RT_RESERVES_OPEN_DEPOSITS, - TALER_EXCHANGEDB_RT_RESERVES_CLOSE_REQUESTS, TALER_EXCHANGEDB_RT_RESERVES_OUT, TALER_EXCHANGEDB_RT_AUDITORS, TALER_EXCHANGEDB_RT_AUDITOR_DENOM_SIGS, @@ -341,14 +340,6 @@ struct TALER_EXCHANGEDB_TableData { struct TALER_ReservePublicKeyP reserve_pub; struct GNUNET_TIME_Timestamp execution_date; - struct TALER_ReserveSignatureP reserve_sig; - struct TALER_PaytoHashP wire_target_h_payto; - } reserves_close_requests; - - struct - { - struct TALER_ReservePublicKeyP reserve_pub; - struct GNUNET_TIME_Timestamp execution_date; struct TALER_WireTransferIdentifierRawP wtid; struct TALER_PaytoHashP sender_account_h_payto; struct TALER_Amount amount; @@ -587,6 +578,8 @@ struct TALER_EXCHANGEDB_TableData struct GNUNET_TIME_Timestamp close_timestamp; struct TALER_ReserveSignatureP reserve_sig; struct TALER_Amount close; + struct TALER_Amount close_fee; + char *payto_uri; } close_requests; struct @@ -4111,6 +4104,24 @@ struct TALER_EXCHANGEDB_Plugin /** + * Obtain information about force-closed reserves + * where the close was not yet done (and their remaining + * balances). Updates the returned reserve's close + * status to "done". + * + * @param cls closure of the plugin + * @param rec function to call on (to be) closed reserves + * @param rec_cls closure for @a rec + * @return transaction status + */ + enum GNUNET_DB_QueryStatus + (*get_unfinished_close_requests)( + void *cls, + TALER_EXCHANGEDB_ReserveExpiredCallback rec, + void *rec_cls); + + + /** * Insert reserve open coin deposit data into database. * Subtracts the @a coin_total from the coin's balance. * diff --git a/src/testing/test_exchange_p2p.c b/src/testing/test_exchange_p2p.c index 1a6c5854f..222cd3d41 100644 --- a/src/testing/test_exchange_p2p.c +++ b/src/testing/test_exchange_p2p.c @@ -446,8 +446,6 @@ run (void *cls, "create-reserve-101", NULL, /* to origin */ MHD_HTTP_OK), -#if FIXME - /* reserve close logic is not yet implemented, hence this fails: */ TALER_TESTING_cmd_exec_closer ("close-reserves-101", config_file, "EUR:1.02", @@ -459,7 +457,6 @@ run (void *cls, "create-reserve-101", "EUR:0", MHD_HTTP_OK), -#endif TALER_TESTING_cmd_end () }; |