-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 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 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 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, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Checking for reserves to close by date %s\n", "Checking for reserves to close by date %s\n",
GNUNET_TIME_timestamp2s (now)); GNUNET_TIME_timestamp2s (now));
qs = db_plugin->get_expired_reserves (db_plugin->cls, 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, now,
&expired_reserve_cb, &expired_reserve_cb,
NULL); NULL);
GNUNET_assert (1 >= qs); }
switch (qs) switch (qs)
{ {
case GNUNET_DB_STATUS_HARD_ERROR: case GNUNET_DB_STATUS_HARD_ERROR:

View File

@ -70,6 +70,8 @@ endif
libtaler_plugin_exchangedb_postgres_la_SOURCES = \ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
plugin_exchangedb_postgres.c pg_helper.h \ plugin_exchangedb_postgres.c pg_helper.h \
pg_do_reserve_open.c pg_do_reserve_open.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_close_request.c pg_insert_close_request.h \
pg_insert_records_by_table.c pg_insert_records_by_table.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 \ 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" "(hc"
",expiration_date" ",expiration_date"
") VALUES " ") VALUES "
"($1, $2);", "($1, $2);"),
2),
/* Used in #postgres_iterate_denomination_info() */ /* Used in #postgres_iterate_denomination_info() */
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"bm_select", "bm_select",
"SELECT" "SELECT"
" expiration_date" " expiration_date"
" FROM benchmap" " FROM benchmap"
" WHERE hc=$1;", " WHERE hc=$1;"),
1),
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"bhm_insert", "bhm_insert",
"INSERT INTO benchhmap " "INSERT INTO benchhmap "
"(hc" "(hc"
",expiration_date" ",expiration_date"
") VALUES " ") VALUES "
"($1, $2);", "($1, $2);"),
2),
/* Used in #postgres_iterate_denomination_info() */ /* Used in #postgres_iterate_denomination_info() */
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"bhm_select", "bhm_select",
"SELECT" "SELECT"
" expiration_date" " expiration_date"
" FROM benchhmap" " FROM benchhmap"
" WHERE hc=$1;", " WHERE hc=$1;"),
1),
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"bem_insert", "bem_insert",
"INSERT INTO benchemap " "INSERT INTO benchemap "
@ -84,16 +80,14 @@ prepare (struct GNUNET_PQ_Context *conn)
",ihc" ",ihc"
",expiration_date" ",expiration_date"
") VALUES " ") VALUES "
"($1, $2, $3);", "($1, $2, $3);"),
3),
/* Used in #postgres_iterate_denomination_info() */ /* Used in #postgres_iterate_denomination_info() */
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"bem_select", "bem_select",
"SELECT" "SELECT"
" expiration_date" " expiration_date"
" FROM benchemap" " FROM benchemap"
" WHERE ihc=$1 AND hc=$2;", " WHERE ihc=$1 AND hc=$2;"),
2),
GNUNET_PQ_PREPARED_STATEMENT_END GNUNET_PQ_PREPARED_STATEMENT_END
}; };
enum GNUNET_GenericReturnValue ret; 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 ------------------------------- ---------------------------- reserves_out -------------------------------
CREATE OR REPLACE FUNCTION create_table_reserves_out( CREATE OR REPLACE FUNCTION create_table_reserves_out(
@ -1752,16 +1701,57 @@ BEGIN
',close_fee_val INT8 NOT NULL' ',close_fee_val INT8 NOT NULL'
',close_fee_frac INT4 NOT NULL' ',close_fee_frac INT4 NOT NULL'
',payto_uri VARCHAR NOT NULL' ',payto_uri VARCHAR NOT NULL'
',done BOOL NOT NULL DEFAULT(FALSE)'
',PRIMARY KEY (reserve_pub,close_timestamp)' ',PRIMARY KEY (reserve_pub,close_timestamp)'
') %s ;' ') %s ;'
,table_name ,table_name
,'PARTITION BY HASH (reserve_pub)' ,'PARTITION BY HASH (reserve_pub)'
,shard_suffix ,shard_suffix
); );
END 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 ------------------------------- ------------------------------- purse_deposits -------------------------------
CREATE OR REPLACE FUNCTION create_table_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'); 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 ---------------------------------------- -- ------------------------------ reserves_out ----------------------------------------
SELECT create_table_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'; IS 'Signature affirming that the reserve is to be closed';
COMMENT ON COLUMN close_requests.close_val 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)'; 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 CREATE TABLE IF NOT EXISTS close_requests_default
PARTITION OF close_requests PARTITION OF close_requests
FOR VALUES WITH (MODULUS 1, REMAINDER 0); FOR VALUES WITH (MODULUS 1, REMAINDER 0);
SELECT add_constraints_to_close_requests_partition('default');
-- ------------------------------ purse_deposits ---------------------------------------- -- ------------------------------ 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. * 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), &td->details.close_requests.reserve_sig),
TALER_PQ_query_param_amount ( TALER_PQ_query_param_amount (
&td->details.close_requests.close), &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 GNUNET_PQ_query_param_end
}; };
@ -1594,8 +1558,11 @@ irbt_cb_table_close_requests (struct PostgresClosure *pg,
",reserve_sig" ",reserve_sig"
",close_val" ",close_val"
",close_frac" ",close_frac"
",close_fee_val"
",close_fee_frac"
",payto_uri"
") VALUES " ") 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, return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_close_requests", "insert_into_table_close_requests",
params); params);
@ -1883,9 +1850,6 @@ TEH_PG_insert_records_by_table (void *cls,
case TALER_EXCHANGEDB_RT_RESERVES_OPEN_DEPOSITS: case TALER_EXCHANGEDB_RT_RESERVES_OPEN_DEPOSITS:
rh = &irbt_cb_table_reserves_open_deposits; rh = &irbt_cb_table_reserves_open_deposits;
break; break;
case TALER_EXCHANGEDB_RT_RESERVES_CLOSE_REQUESTS:
rh = &irbt_cb_table_reserves_close_requests;
break;
case TALER_EXCHANGEDB_RT_RESERVES_OUT: case TALER_EXCHANGEDB_RT_RESERVES_OUT:
rh = &irbt_cb_table_reserves_out; rh = &irbt_cb_table_reserves_out;
break; break;

View File

@ -1867,12 +1867,21 @@ lrbt_cb_table_close_requests (void *cls,
GNUNET_PQ_result_spec_auto_from_type ( GNUNET_PQ_result_spec_auto_from_type (
"reserve_pub", "reserve_pub",
&td.details.close_requests.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 ( GNUNET_PQ_result_spec_auto_from_type (
"reserve_sig", "reserve_sig",
&td.details.close_requests.reserve_sig), &td.details.close_requests.reserve_sig),
TALER_PQ_RESULT_SPEC_AMOUNT ( TALER_PQ_RESULT_SPEC_AMOUNT (
"close", "close",
&td.details.close_requests.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 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" " ORDER BY open_request_uuid DESC"
" LIMIT 1;"); " LIMIT 1;");
break; 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: case TALER_EXCHANGEDB_RT_RESERVES_OUT:
XPREPARE ("select_serial_by_table_reserves_out", XPREPARE ("select_serial_by_table_reserves_out",
"SELECT" "SELECT"

View File

@ -31,6 +31,8 @@
#include "taler_exchangedb_plugin.h" #include "taler_exchangedb_plugin.h"
#include "pg_helper.h" #include "pg_helper.h"
#include "pg_do_reserve_open.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_close_request.h"
#include "pg_insert_records_by_table.h" #include "pg_insert_records_by_table.h"
#include "pg_insert_reserve_open_deposit.h" #include "pg_insert_reserve_open_deposit.h"
@ -2344,32 +2346,6 @@ prepare_statements (struct PostgresClosure *pg)
" FROM history_requests" " FROM history_requests"
" WHERE reserve_pub=$1" " WHERE reserve_pub=$1"
" AND request_timestamp>=$2;"), " 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 /* Used in #postgres_get_coin_transactions() to obtain recoup transactions
for a coin */ for a coin */
GNUNET_PQ_make_prepare ( 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. * 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_wire_fee = &postgres_get_wire_fee;
plugin->get_global_fee = &postgres_get_global_fee; plugin->get_global_fee = &postgres_get_global_fee;
plugin->get_global_fees = &postgres_get_global_fees; 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->insert_reserve_closed = &postgres_insert_reserve_closed;
plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert; plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert;
plugin->wire_prepare_data_mark_finished = plugin->wire_prepare_data_mark_finished =
@ -15290,6 +15133,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
/* NEW style, sort alphabetically! */ /* NEW style, sort alphabetically! */
plugin->do_reserve_open plugin->do_reserve_open
= &TEH_PG_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 plugin->insert_records_by_table
= &TEH_PG_insert_records_by_table; = &TEH_PG_insert_records_by_table;
plugin->insert_reserve_open_deposit plugin->insert_reserve_open_deposit

View File

@ -50,9 +50,6 @@ BEGIN
PERFORM create_table_reserves_open_deposits(shard_suffix); PERFORM create_table_reserves_open_deposits(shard_suffix);
PERFORM add_constraints_to_reserves_open_deposits_partition(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 create_table_reserves_out(shard_suffix);
PERFORM add_constraints_to_reserves_out_partition(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_history_requests(shard_suffix);
PERFORM create_table_close_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 create_table_purse_deposits(shard_suffix);
PERFORM add_constraints_to_purse_deposits_partition(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_CLOSE,
TALER_EXCHANGEDB_RT_RESERVES_OPEN_REQUESTS, TALER_EXCHANGEDB_RT_RESERVES_OPEN_REQUESTS,
TALER_EXCHANGEDB_RT_RESERVES_OPEN_DEPOSITS, TALER_EXCHANGEDB_RT_RESERVES_OPEN_DEPOSITS,
TALER_EXCHANGEDB_RT_RESERVES_CLOSE_REQUESTS,
TALER_EXCHANGEDB_RT_RESERVES_OUT, TALER_EXCHANGEDB_RT_RESERVES_OUT,
TALER_EXCHANGEDB_RT_AUDITORS, TALER_EXCHANGEDB_RT_AUDITORS,
TALER_EXCHANGEDB_RT_AUDITOR_DENOM_SIGS, TALER_EXCHANGEDB_RT_AUDITOR_DENOM_SIGS,
@ -337,14 +336,6 @@ struct TALER_EXCHANGEDB_TableData
struct TALER_Amount contribution; struct TALER_Amount contribution;
} reserves_open_deposits; } 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
{ {
struct TALER_ReservePublicKeyP reserve_pub; struct TALER_ReservePublicKeyP reserve_pub;
@ -587,6 +578,8 @@ struct TALER_EXCHANGEDB_TableData
struct GNUNET_TIME_Timestamp close_timestamp; struct GNUNET_TIME_Timestamp close_timestamp;
struct TALER_ReserveSignatureP reserve_sig; struct TALER_ReserveSignatureP reserve_sig;
struct TALER_Amount close; struct TALER_Amount close;
struct TALER_Amount close_fee;
char *payto_uri;
} close_requests; } close_requests;
struct struct
@ -4110,6 +4103,24 @@ struct TALER_EXCHANGEDB_Plugin
void *rec_cls); 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. * Insert reserve open coin deposit data into database.
* Subtracts the @a coin_total from the coin's balance. * Subtracts the @a coin_total from the coin's balance.

View File

@ -446,8 +446,6 @@ run (void *cls,
"create-reserve-101", "create-reserve-101",
NULL, /* to origin */ NULL, /* to origin */
MHD_HTTP_OK), MHD_HTTP_OK),
#if FIXME
/* reserve close logic is not yet implemented, hence this fails: */
TALER_TESTING_cmd_exec_closer ("close-reserves-101", TALER_TESTING_cmd_exec_closer ("close-reserves-101",
config_file, config_file,
"EUR:1.02", "EUR:1.02",
@ -459,7 +457,6 @@ run (void *cls,
"create-reserve-101", "create-reserve-101",
"EUR:0", "EUR:0",
MHD_HTTP_OK), MHD_HTTP_OK),
#endif
TALER_TESTING_cmd_end () TALER_TESTING_cmd_end ()
}; };