-implement reserve closure in test

This commit is contained in:
Christian Grothoff 2022-10-13 19:07:25 +02:00
parent 4fc77b9dbf
commit 09310cc66e
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
16 changed files with 537 additions and 312 deletions

View File

@ -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:

View File

@ -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 \

View File

@ -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;

View File

@ -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(

View File

@ -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 ----------------------------------------

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -429,46 +429,6 @@ irbt_cb_table_reserves_open_deposits (
}
/**
* 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_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.
*
@ -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;

View File

@ -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
};

View File

@ -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"

View File

@ -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 (
@ -8550,138 +8526,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.
*
@ -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

View File

@ -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);

View File

@ -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,
@ -337,14 +336,6 @@ struct TALER_EXCHANGEDB_TableData
struct TALER_Amount contribution;
} reserves_open_deposits;
struct
{
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;
@ -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
@ -4110,6 +4103,24 @@ struct TALER_EXCHANGEDB_Plugin
void *rec_cls);
/**
* 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.

View File

@ -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 ()
};