aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2022-10-13 19:07:25 +0200
committerChristian Grothoff <christian@grothoff.org>2022-10-13 19:07:25 +0200
commit09310cc66ebbdf083c4b4fa86a368b3ea52c0c16 (patch)
treeb87132659d9d27e5e7d84e4f76b4d836b6689f9d
parent4fc77b9dbfee88dff2d366d7dbb2d91797f8b9a0 (diff)
-implement reserve closure in test
-rw-r--r--src/exchange/taler-exchange-closer.c19
-rw-r--r--src/exchangedb/Makefile.am2
-rw-r--r--src/exchangedb/bench_db.c18
-rw-r--r--src/exchangedb/common-0001.sql92
-rw-r--r--src/exchangedb/exchange-0001-part.sql19
-rw-r--r--src/exchangedb/pg_get_expired_reserves.c173
-rw-r--r--src/exchangedb/pg_get_expired_reserves.h45
-rw-r--r--src/exchangedb/pg_get_unfinished_close_requests.c162
-rw-r--r--src/exchangedb/pg_get_unfinished_close_requests.h46
-rw-r--r--src/exchangedb/pg_insert_records_by_table.c52
-rw-r--r--src/exchangedb/pg_lookup_records_by_table.c9
-rw-r--r--src/exchangedb/pg_lookup_serial_by_table.c8
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c165
-rw-r--r--src/exchangedb/shard-0001-part.sql5
-rw-r--r--src/include/taler_exchangedb_plugin.h29
-rw-r--r--src/testing/test_exchange_p2p.c3
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 ()
};