exchangedb: use partial index instead of materialized tables deposits_by_ready and deposits_by_matching; remove now broken code; rename benchmarks to use perf_-prefix and correspond to function they benchmark

This commit is contained in:
Christian Grothoff 2023-02-05 19:11:47 +01:00
parent e66087987f
commit ab03ba16e9
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
22 changed files with 1508 additions and 3501 deletions

View File

@ -495,11 +495,13 @@ transaction_completed (void)
* We got incoming transaction details from the bank. Add them * We got incoming transaction details from the bank. Add them
* to the database. * to the database.
* *
* @param batch_size desired batch size
* @param details array of transaction details * @param details array of transaction details
* @param details_length length of the @a details array * @param details_length length of the @a details array
*/ */
static void static void
process_reply (const struct TALER_BANK_CreditDetails *details, process_reply (unsigned int batch_size,
const struct TALER_BANK_CreditDetails *details,
unsigned int details_length) unsigned int details_length)
{ {
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
@ -545,416 +547,31 @@ process_reply (const struct TALER_BANK_CreditDetails *details,
} }
lroff = cd->serial_id; lroff = cd->serial_id;
} }
if (GNUNET_OK != if (0 != details_length)
db_plugin->start_read_committed (db_plugin->cls,
"wirewatch check for incoming wire transfers"))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, enum GNUNET_DB_QueryStatus qss[details_length];
"Failed to start database transaction!\n"); struct TALER_EXCHANGEDB_ReserveInInfo reserves[details_length];
global_ret = EXIT_FAILURE;
GNUNET_SCHEDULER_shutdown ();
return;
}
started_transaction = true;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Importing %u transactions\n",
details_length);
for (unsigned int i = 0; i<details_length; i++)
{
const struct TALER_BANK_CreditDetails *cd = &details[i];
/* FIXME #7276: Consider using Postgres multi-valued insert here, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
for up to 15x speed-up according to "Importing %u transactions\n",
https://dba.stackexchange.com/questions/224989/multi-row-insert-vs-transactional-single-row-inserts#225006 details_length);
(Note: this may require changing both the for (unsigned int i = 0; i<details_length; i++)
plugin API as well as modifying how this function is called.) */ {
const struct TALER_BANK_CreditDetails *cd = &details[i];
struct TALER_EXCHANGEDB_ReserveInInfo *res = &reserves[i];
res->reserve_pub = &cd->reserve_pub;
res->balance = &cd->amount;
res->execution_time = cd->execution_date;
res->sender_account_details = cd->debit_account_uri;
res->exchange_account_name = ai->section_name;
res->wire_reference = cd->serial_id;
}
qs = db_plugin->reserves_in_insert (db_plugin->cls, qs = db_plugin->reserves_in_insert (db_plugin->cls,
&cd->reserve_pub, reserves,
&cd->amount, details_length,
cd->execution_date, batch_size,
cd->debit_account_uri, qss);
ai->section_name,
cd->serial_id);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
GNUNET_SCHEDULER_shutdown ();
return;
case GNUNET_DB_STATUS_SOFT_ERROR:
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got DB soft error for reserves_in_insert. Rolling back.\n");
handle_soft_error ();
return;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
/* Either wirewatch was freshly started after the system was
shutdown and we're going over an incomplete shard again
after being restarted, or the shard lock period was too
short (number of workers set incorrectly?) and a 2nd
wirewatcher has been stealing our work while we are still
at it. */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Attempted to import transaction %llu (%s) twice. "
"This should happen rarely (if not, ask for support).\n",
(unsigned long long) cd->serial_id,
job_name);
break;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Imported transaction %llu.",
(unsigned long long) cd->serial_id);
/* normal case */
progress = true;
break;
}
}
latest_row_off = lroff;
shard_done = (shard_end <= latest_row_off);
if (shard_done)
{
/* shard is complete, mark this as well */
qs = db_plugin->complete_shard (db_plugin->cls,
job_name,
shard_start,
shard_end);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
GNUNET_SCHEDULER_shutdown ();
return;
case GNUNET_DB_STATUS_SOFT_ERROR:
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got DB soft error for complete_shard. Rolling back.\n");
handle_soft_error ();
return;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
GNUNET_break (0);
/* Not expected, but let's just continue */
break;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
/* normal case */
progress = true;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Completed shard %s (%llu,%llu] after %s\n",
job_name,
(unsigned long long) shard_start,
(unsigned long long) shard_end,
GNUNET_STRINGS_relative_time_to_string (
GNUNET_TIME_absolute_get_duration (shard_start_time),
true));
break;
}
}
if (! progress)
{
db_plugin->rollback (db_plugin->cls);
}
else
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Committing %s progress (%llu,%llu] at %llu\n (%s)",
job_name,
(unsigned long long) shard_start,
(unsigned long long) shard_end,
(unsigned long long) latest_row_off,
shard_done
? "shard done"
: "shard incomplete");
qs = db_plugin->commit (db_plugin->cls);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
GNUNET_SCHEDULER_shutdown ();
return;
case GNUNET_DB_STATUS_SOFT_ERROR:
/* reduce transaction size to reduce rollback probability */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got DB soft error on commit. Reducing transaction size.\n");
handle_soft_error ();
return;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
started_transaction = false;
/* normal case */
break;
}
}
if (shard_done)
{
shard_delay = GNUNET_TIME_absolute_get_duration (shard_start_time);
shard_open = false;
transaction_completed ();
return;
}
GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&continue_with_shard,
NULL);
}
/**
* We got incoming transaction details from the bank. Add them
* to the database.
*
* @param details array of transaction details
* @param details_length length of the @a details array
*/
static void
process_reply_batched (const struct TALER_BANK_CreditDetails *details,
unsigned int details_length)
{
enum GNUNET_DB_QueryStatus qs;
bool shard_done;
uint64_t lroff = latest_row_off;
if (0 == details_length)
{
/* Server should have used 204, not 200! */
GNUNET_break_op (0);
transaction_completed ();
return;
}
/* check serial IDs for range constraints */
for (unsigned int i = 0; i<details_length; i++)
{
const struct TALER_BANK_CreditDetails *cd = &details[i];
if (cd->serial_id < lroff)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Serial ID %llu not monotonic (got %llu before). Failing!\n",
(unsigned long long) cd->serial_id,
(unsigned long long) lroff);
db_plugin->rollback (db_plugin->cls);
GNUNET_SCHEDULER_shutdown ();
return;
}
if (cd->serial_id > shard_end)
{
/* we are *past* the current shard (likely because the serial_id of the
shard_end happens to not exist in the DB). So commit and stop this
iteration! */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Serial ID %llu past shard end at %llu, ending iteration early!\n",
(unsigned long long) cd->serial_id,
(unsigned long long) shard_end);
details_length = i;
progress = true;
lroff = cd->serial_id - 1;
break;
}
lroff = cd->serial_id;
}
if (0 != details_length)
{
enum GNUNET_DB_QueryStatus qss[details_length];
struct TALER_EXCHANGEDB_ReserveInInfo reserves[details_length];
hh_returned_data = true;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Importing %u transactions\n",
details_length);
for (unsigned int i = 0; i<details_length; i++)
{
const struct TALER_BANK_CreditDetails *cd = &details[i];
struct TALER_EXCHANGEDB_ReserveInInfo *res = &reserves[i];
res->reserve_pub = &cd->reserve_pub;
res->balance = &cd->amount;
res->execution_time = cd->execution_date;
res->sender_account_details = cd->debit_account_uri;
res->exchange_account_name = ai->section_name;
res->wire_reference = cd->serial_id;
}
qs = db_plugin->batch_reserves_in_insert (db_plugin->cls,
reserves,
details_length,
qss);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
GNUNET_SCHEDULER_shutdown ();
return;
case GNUNET_DB_STATUS_SOFT_ERROR:
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got DB soft error for batch_reserves_in_insert. Rolling back.\n");
handle_soft_error ();
return;
default:
break;
}
for (unsigned int i = 0; i<details_length; i++)
{
const struct TALER_BANK_CreditDetails *cd = &details[i];
switch (qss[i])
{
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
GNUNET_SCHEDULER_shutdown ();
return;
case GNUNET_DB_STATUS_SOFT_ERROR:
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got DB soft error for batch_reserves_in_insert(%u). Rolling back.\n",
i);
handle_soft_error ();
return;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
/* Either wirewatch was freshly started after the system was
shutdown and we're going over an incomplete shard again
after being restarted, or the shard lock period was too
short (number of workers set incorrectly?) and a 2nd
wirewatcher has been stealing our work while we are still
at it. */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Attempted to import transaction %llu (%s) twice. "
"This should happen rarely (if not, ask for support).\n",
(unsigned long long) cd->serial_id,
job_name);
break;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Imported transaction %llu.",
(unsigned long long) cd->serial_id);
/* normal case */
progress = true;
break;
}
}
}
latest_row_off = lroff;
shard_done = (shard_end <= latest_row_off);
if (shard_done)
{
/* shard is complete, mark this as well */
qs = db_plugin->complete_shard (db_plugin->cls,
job_name,
shard_start,
shard_end);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
GNUNET_SCHEDULER_shutdown ();
return;
case GNUNET_DB_STATUS_SOFT_ERROR:
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got DB soft error for complete_shard. Rolling back.\n");
handle_soft_error ();
return;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
GNUNET_break (0);
/* Not expected, but let's just continue */
break;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
/* normal case */
progress = true;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Completed shard %s (%llu,%llu] after %s\n",
job_name,
(unsigned long long) shard_start,
(unsigned long long) shard_end,
GNUNET_STRINGS_relative_time_to_string (
GNUNET_TIME_absolute_get_duration (shard_start_time),
true));
break;
}
shard_delay = GNUNET_TIME_absolute_get_duration (shard_start_time);
shard_open = false;
transaction_completed ();
return;
}
GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&continue_with_shard,
NULL);
}
/**
* We got incoming transaction details from the bank. Add them
* to the database.
*
* @param batch_size desired batch size
* @param details array of transaction details
* @param details_length length of the @a details array
*/
static void
process_reply_batched2 (unsigned int batch_size,
const struct TALER_BANK_CreditDetails *details,
unsigned int details_length)
{
enum GNUNET_DB_QueryStatus qs;
bool shard_done;
uint64_t lroff = latest_row_off;
if (0 == details_length)
{
/* Server should have used 204, not 200! */
GNUNET_break_op (0);
transaction_completed ();
return;
}
hh_returned_data = true;
/* check serial IDs for range constraints */
for (unsigned int i = 0; i<details_length; i++)
{
const struct TALER_BANK_CreditDetails *cd = &details[i];
if (cd->serial_id < lroff)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Serial ID %llu not monotonic (got %llu before). Failing!\n",
(unsigned long long) cd->serial_id,
(unsigned long long) lroff);
db_plugin->rollback (db_plugin->cls);
GNUNET_SCHEDULER_shutdown ();
return;
}
if (cd->serial_id > shard_end)
{
/* we are *past* the current shard (likely because the serial_id of the
shard_end happens to not exist in the DB). So commit and stop this
iteration! */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Serial ID %llu past shard end at %llu, ending iteration early!\n",
(unsigned long long) cd->serial_id,
(unsigned long long) shard_end);
details_length = i;
progress = true;
lroff = cd->serial_id - 1;
break;
}
lroff = cd->serial_id;
}
if (0 != details_length)
{
enum GNUNET_DB_QueryStatus qss[details_length];
struct TALER_EXCHANGEDB_ReserveInInfo reserves[details_length];
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Importing %u transactions\n",
details_length);
for (unsigned int i = 0; i<details_length; i++)
{
const struct TALER_BANK_CreditDetails *cd = &details[i];
struct TALER_EXCHANGEDB_ReserveInInfo *res = &reserves[i];
res->reserve_pub = &cd->reserve_pub;
res->balance = &cd->amount;
res->execution_time = cd->execution_date;
res->sender_account_details = cd->debit_account_uri;
res->exchange_account_name = ai->section_name;
res->wire_reference = cd->serial_id;
}
qs = db_plugin->batch2_reserves_in_insert (db_plugin->cls,
reserves,
details_length,
batch_size,
qss);
switch (qs) switch (qs)
{ {
case GNUNET_DB_STATUS_HARD_ERROR: case GNUNET_DB_STATUS_HARD_ERROR:
@ -1074,7 +691,7 @@ history_cb (void *cls,
(void) cls; (void) cls;
if (-2 == batch_mode) if (-2 == batch_mode)
{ {
const char *mode = getenv ("TALER_USE_BATCH"); const char *mode = getenv ("TALER_WIREWATCH_BATCH_SIZE");
char dummy; char dummy;
if ( (NULL == mode) || if ( (NULL == mode) ||
@ -1087,7 +704,7 @@ history_cb (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Bad batch mode `%s' specified\n", "Bad batch mode `%s' specified\n",
mode); mode);
batch_mode = -1; batch_mode = 8; /* maximum supported is currently 8 */
} }
} }
GNUNET_assert (NULL == task); GNUNET_assert (NULL == task);
@ -1098,22 +715,9 @@ history_cb (void *cls,
switch (reply->http_status) switch (reply->http_status)
{ {
case MHD_HTTP_OK: case MHD_HTTP_OK:
switch (batch_mode) process_reply (batch_mode,
{ reply->details.success.details,
case -1: reply->details.success.details_length);
process_reply (reply->details.success.details,
reply->details.success.details_length);
break;
case 0:
process_reply_batched (reply->details.success.details,
reply->details.success.details_length);
break;
default:
process_reply_batched2 ((unsigned int) batch_mode,
reply->details.success.details,
reply->details.success.details_length);
break;
}
return; return;
case MHD_HTTP_NO_CONTENT: case MHD_HTTP_NO_CONTENT:
transaction_completed (); transaction_completed ();

View File

@ -1,17 +1,9 @@
test-exchangedb-auditors
test-exchangedb-denomkeys
test-exchangedb-fees
test-exchangedb-postgres test-exchangedb-postgres
test-exchangedb-signkeys
test-perf-taler-exchangedb
bench-db-postgres bench-db-postgres
shard-drop0001.sqltest-exchangedb-by-j-postgres perf_deposits_get_ready-postgres
test-exchangedb-by-j-postgres perf_get_link_data-postgres
perf-exchangedb-reserves-in-insert-postgres perf_reserves_in_insert-postgres
perf_select_refunds_by_coin-postgres
exchange-0002.sql exchange-0002.sql
procedures.sql procedures.sql
exchange-0003.sql exchange-0003.sql
test-exchangedb-batch-reserves-in-insert-postgres
test-exchangedb-populate-table-postgres
test-exchangedb-populate-link-data-postgres
test-exchangedb-populate-ready-deposit-postgres

View File

@ -1,6 +1,6 @@
-- --
-- This file is part of TALER -- This file is part of TALER
-- Copyright (C) 2014--2022 Taler Systems SA -- Copyright (C) 2014--2023 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 General Public License as published by the Free Software -- terms of the GNU General Public License as published by the Free Software
@ -116,6 +116,22 @@ BEGIN
',ADD CONSTRAINT ' || table_name || '_coin_pub_merchant_pub_h_contract_terms_key' ',ADD CONSTRAINT ' || table_name || '_coin_pub_merchant_pub_h_contract_terms_key'
' UNIQUE (coin_pub, merchant_pub, h_contract_terms)' ' UNIQUE (coin_pub, merchant_pub, h_contract_terms)'
); );
EXECUTE FORMAT (
'CREATE INDEX ' || table_name || '_by_ready '
'ON ' || table_name || ' '
'(wire_deadline ASC'
',shard ASC'
',coin_pub'
') WHERE NOT (done OR policy_blocked);'
);
EXECUTE FORMAT (
'CREATE INDEX ' || table_name || '_for_matching '
'ON ' || table_name || ' '
'(refund_deadline ASC'
',merchant_pub'
',coin_pub'
') WHERE NOT (done OR policy_blocked);'
);
END END
$$; $$;
@ -399,29 +415,5 @@ INSERT INTO exchange_tables
,'exchange-0002' ,'exchange-0002'
,'foreign' ,'foreign'
,TRUE ,TRUE
,FALSE), ,FALSE)
('deposits_by_ready' ;
,'exchange-0002'
,'create'
,TRUE
,TRUE),
('deposits_by_ready'
,'exchange-0002'
,'constrain'
,TRUE
,TRUE),
('deposits_for_matching'
,'exchange-0002'
,'create'
,TRUE
,TRUE),
('deposits_for_matching'
,'exchange-0002'
,'constrain'
,TRUE
,TRUE),
('deposits'
,'exchange-0002'
,'master'
,TRUE
,FALSE);

View File

@ -255,8 +255,6 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_select_purse_deposits_above_serial_id.h pg_select_purse_deposits_above_serial_id.c \ pg_select_purse_deposits_above_serial_id.h pg_select_purse_deposits_above_serial_id.c \
pg_select_account_merges_above_serial_id.h pg_select_account_merges_above_serial_id.c \ pg_select_account_merges_above_serial_id.h pg_select_account_merges_above_serial_id.c \
pg_select_all_purse_decisions_above_serial_id.h pg_select_all_purse_decisions_above_serial_id.c \ pg_select_all_purse_decisions_above_serial_id.h pg_select_all_purse_decisions_above_serial_id.c \
pg_batch_reserves_in_insert.h pg_batch_reserves_in_insert.c \
pg_batch2_reserves_in_insert.h pg_batch2_reserves_in_insert.c \
pg_select_reserve_open_above_serial_id.c pg_select_reserve_open_above_serial_id.h pg_select_reserve_open_above_serial_id.c pg_select_reserve_open_above_serial_id.h
libtaler_plugin_exchangedb_postgres_la_LIBADD = \ libtaler_plugin_exchangedb_postgres_la_LIBADD = \
$(LTLIBINTL) $(LTLIBINTL)
@ -295,22 +293,14 @@ check_PROGRAMS = \
noinst_PROGRAMS = \ noinst_PROGRAMS = \
bench-db-postgres\ bench-db-postgres\
perf-exchangedb-reserves-in-insert-postgres\ perf_get_link_data-postgres\
test-exchangedb-by-j-postgres\ perf_select_refunds_by_coin-postgres\
test-exchangedb-batch-reserves-in-insert-postgres\ perf_reserves_in_insert-postgres \
test-exchangedb-populate-select-refunds-by-coin-postgres\ perf_deposits_get_ready-postgres
test-exchangedb-populate-link-data-postgres\
test-exchangedb-populate-ready-deposit-postgres
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH; AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
TESTS = \ TESTS = \
test-exchangedb-postgres\ (check_PROGRAMS)
test-exchangedb-by-j-postgres\
perf-exchangedb-reserves-in-insert-postgres\
test-exchangedb-batch-reserves-in-insert-postgres\
test-exchangedb-populate-select-refunds-by-coin-postgres\
test-exchangedb-populate-link-data-postgres\
test-exchangedb-populate-ready-deposit-postgres
test_exchangedb_postgres_SOURCES = \ test_exchangedb_postgres_SOURCES = \
test_exchangedb.c test_exchangedb.c
test_exchangedb_postgres_LDADD = \ test_exchangedb_postgres_LDADD = \
@ -323,32 +313,6 @@ test_exchangedb_postgres_LDADD = \
-lgnunetutil \ -lgnunetutil \
$(XLIB) $(XLIB)
test_exchangedb_by_j_postgres_SOURCES = \
test_exchangedb_by_j.c
test_exchangedb_by_j_postgres_LDADD = \
libtalerexchangedb.la \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/pq/libtalerpq.la \
-ljansson \
-lgnunetjson \
-lgnunetutil \
-lm \
$(XLIB)
perf_exchangedb_reserves_in_insert_postgres_SOURCES = \
perf_exchangedb_reserves_in_insert.c
perf_exchangedb_reserves_in_insert_postgres_LDADD = \
libtalerexchangedb.la \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/pq/libtalerpq.la \
-ljansson \
-lgnunetjson \
-lgnunetutil \
$(XLIB)
bench_db_postgres_SOURCES = \ bench_db_postgres_SOURCES = \
bench_db.c bench_db.c
bench_db_postgres_LDADD = \ bench_db_postgres_LDADD = \
@ -359,21 +323,9 @@ bench_db_postgres_LDADD = \
-lgnunetutil \ -lgnunetutil \
$(XLIB) $(XLIB)
test_exchangedb_batch_reserves_in_insert_postgres_SOURCES = \ perf_reserves_in_insert_postgres_SOURCES = \
test_exchangedb_batch_reserves_in_insert.c perf_reserves_in_insert.c
test_exchangedb_batch_reserves_in_insert_postgres_LDADD = \ perf_reserves_in_insert_postgres_LDADD = \
libtalerexchangedb.la \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/pq/libtalerpq.la \
-ljansson \
-lgnunetjson \
-lgnunetutil \
$(XLIB)
test_exchangedb_populate_select_refunds_by_coin_postgres_SOURCES = \
test_exchangedb_populate_select_refunds_by_coin.c
test_exchangedb_populate_select_refunds_by_coin_postgres_LDADD = \
libtalerexchangedb.la \ libtalerexchangedb.la \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/util/libtalerutil.la \
@ -384,9 +336,9 @@ test_exchangedb_populate_select_refunds_by_coin_postgres_LDADD = \
-lm \ -lm \
$(XLIB) $(XLIB)
test_exchangedb_populate_link_data_postgres_SOURCES = \ perf_select_refunds_by_coin_postgres_SOURCES = \
test_exchangedb_populate_link_data.c perf_select_refunds_by_coin.c
test_exchangedb_populate_link_data_postgres_LDADD = \ perf_select_refunds_by_coin_postgres_LDADD = \
libtalerexchangedb.la \ libtalerexchangedb.la \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/util/libtalerutil.la \
@ -397,9 +349,22 @@ test_exchangedb_populate_link_data_postgres_LDADD = \
-lm \ -lm \
$(XLIB) $(XLIB)
test_exchangedb_populate_ready_deposit_postgres_SOURCES = \ perf_get_link_data_postgres_SOURCES = \
test_exchangedb_populate_ready_deposit.c perf_get_link_data.c
test_exchangedb_populate_ready_deposit_postgres_LDADD = \ perf_get_link_data_postgres_LDADD = \
libtalerexchangedb.la \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/pq/libtalerpq.la \
-ljansson \
-lgnunetjson \
-lgnunetutil \
-lm \
$(XLIB)
perf_deposits_get_ready_postgres_SOURCES = \
perf_deposits_get_ready.c
perf_deposits_get_ready_postgres_LDADD = \
libtalerexchangedb.la \ libtalerexchangedb.la \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/util/libtalerutil.la \

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2022 Taler Systems SA Copyright (C) 2014-2023 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 General Public License as published by the Free Software terms of the GNU General Public License as published by the Free Software
@ -14,8 +14,8 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/** /**
* @file exchangedb/test_exchangedb_populate_ready_deposit.c * @file exchangedb/perf_deposits_get_ready.c
* @brief test cases for DB interaction functions * @brief benchmark for deposits_get_ready
* @author Joseph Xu * @author Joseph Xu
*/ */
#include "platform.h" #include "platform.h"
@ -34,7 +34,7 @@ static int result;
*/ */
#define FAILIF(cond) \ #define FAILIF(cond) \
do { \ do { \
if (! (cond)) {break;} \ if (! (cond)) {break;} \
GNUNET_break (0); \ GNUNET_break (0); \
goto drop; \ goto drop; \
} while (0) } while (0)
@ -57,21 +57,27 @@ static int result;
*/ */
#define CURRENCY "EUR" #define CURRENCY "EUR"
#define RSA_KEY_SIZE 1024 #define RSA_KEY_SIZE 1024
#define NUM_ROWS 1000000 #define NUM_ROWS 1000
#define ROUNDS 10000 #define ROUNDS 100
#define MELT_NEW_COINS 5 #define MELT_NEW_COINS 5
#define MELT_NOREVEAL_INDEX 1 #define MELT_NOREVEAL_INDEX 1
/** /**
* Database plugin under test. * Database plugin under test.
*/ */
static struct TALER_EXCHANGEDB_Plugin *plugin; static struct TALER_EXCHANGEDB_Plugin *plugin;
static struct TALER_DenomFeeSet fees; static struct TALER_DenomFeeSet fees;
static struct TALER_MerchantWireHashP h_wire_wt; static struct TALER_MerchantWireHashP h_wire_wt;
/** /**
* Denomination keys used for fresh coins in melt test. * Denomination keys used for fresh coins in melt test.
*/ */
static struct DenomKeyPair **new_dkp; static struct DenomKeyPair **new_dkp;
static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins; static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins;
struct DenomKeyPair struct DenomKeyPair
{ {
struct TALER_DenominationPrivateKey priv; struct TALER_DenominationPrivateKey priv;
@ -173,8 +179,6 @@ create_denom_key_pair (unsigned int size,
} }
/** /**
* Main function that will be run by the scheduler. * Main function that will be run by the scheduler.
* *
@ -191,19 +195,19 @@ run (void *cls)
struct TALER_EXCHANGEDB_CollectableBlindcoin cbc; struct TALER_EXCHANGEDB_CollectableBlindcoin cbc;
struct TALER_DenominationPublicKey *new_denom_pubs = NULL; struct TALER_DenominationPublicKey *new_denom_pubs = NULL;
struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO; struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO;
unsigned long long sqrs=0; unsigned long long sqrs = 0;
struct TALER_EXCHANGEDB_Deposit *depos=NULL; struct TALER_EXCHANGEDB_Deposit *depos = NULL;
struct TALER_EXCHANGEDB_Refund *ref=NULL; struct TALER_EXCHANGEDB_Refund *ref = NULL;
unsigned int *perm; unsigned int *perm;
unsigned long long duration_sq; unsigned long long duration_sq;
struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin; struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin;
struct TALER_ExchangeWithdrawValues alg_values = { struct TALER_ExchangeWithdrawValues alg_values = {
.cipher = TALER_DENOMINATION_RSA .cipher = TALER_DENOMINATION_RSA
}; };
ref = GNUNET_new_array (ROUNDS +1, ref = GNUNET_new_array (ROUNDS + 1,
struct TALER_EXCHANGEDB_Refund); struct TALER_EXCHANGEDB_Refund);
depos = GNUNET_new_array (ROUNDS +1, depos = GNUNET_new_array (ROUNDS + 1,
struct TALER_EXCHANGEDB_Deposit); struct TALER_EXCHANGEDB_Deposit);
if (NULL == if (NULL ==
@ -245,194 +249,190 @@ run (void *cls)
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":0.000010", TALER_string_to_amount (CURRENCY ":0.000010",
&fees.refund)); &fees.refund));
//DENOMINATION
{ {
ZR_BLK (&cbc); ZR_BLK (&cbc);
//PAIR KEY LIST
new_dkp = GNUNET_new_array (MELT_NEW_COINS, new_dkp = GNUNET_new_array (MELT_NEW_COINS,
struct DenomKeyPair *); struct DenomKeyPair *);
//PUBLIC KEY LIST
new_denom_pubs = GNUNET_new_array (MELT_NEW_COINS, new_denom_pubs = GNUNET_new_array (MELT_NEW_COINS,
struct TALER_DenominationPublicKey); struct TALER_DenominationPublicKey);
//REFRESH REVEAL COIN LIST
revealed_coins revealed_coins
= GNUNET_new_array (MELT_NEW_COINS, = GNUNET_new_array (MELT_NEW_COINS,
struct TALER_EXCHANGEDB_RefreshRevealedCoin); struct TALER_EXCHANGEDB_RefreshRevealedCoin);
for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++) for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++)
{ {
struct GNUNET_TIME_Timestamp now; struct GNUNET_TIME_Timestamp now;
struct TALER_BlindedRsaPlanchet *rp; struct TALER_BlindedRsaPlanchet *rp;
struct TALER_BlindedPlanchet *bp; struct TALER_BlindedPlanchet *bp;
now = GNUNET_TIME_timestamp_get (); now = GNUNET_TIME_timestamp_get ();
//5 KEY PAIR new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE,
new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE, now,
now, &value,
&value, &fees);
&fees); GNUNET_assert (NULL != new_dkp[cnt]);
GNUNET_assert (NULL != new_dkp[cnt]); new_denom_pubs[cnt] = new_dkp[cnt]->pub;
new_denom_pubs[cnt] = new_dkp[cnt]->pub; ccoin = &revealed_coins[cnt];
ccoin = &revealed_coins[cnt]; bp = &ccoin->blinded_planchet;
bp = &ccoin->blinded_planchet; bp->cipher = TALER_DENOMINATION_RSA;
bp->cipher = TALER_DENOMINATION_RSA; rp = &bp->details.rsa_blinded_planchet;
rp = &bp->details.rsa_blinded_planchet; rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 (
rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 ( GNUNET_CRYPTO_QUALITY_WEAK,
GNUNET_CRYPTO_QUALITY_WEAK, (RSA_KEY_SIZE / 8) - 1);
(RSA_KEY_SIZE / 8) - 1); rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size);
rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, rp->blinded_msg,
rp->blinded_msg, rp->blinded_msg_size);
rp->blinded_msg_size); TALER_denom_pub_hash (&new_dkp[cnt]->pub,
TALER_denom_pub_hash (&new_dkp[cnt]->pub, &ccoin->h_denom_pub);
&ccoin->h_denom_pub); ccoin->exchange_vals = alg_values;
ccoin->exchange_vals = alg_values; TALER_coin_ev_hash (bp,
TALER_coin_ev_hash (bp, &ccoin->h_denom_pub,
&ccoin->h_denom_pub, &ccoin->coin_envelope_hash);
&ccoin->coin_envelope_hash); GNUNET_assert (GNUNET_OK ==
GNUNET_assert (GNUNET_OK ==
TALER_denom_sign_blinded (&ccoin->coin_sig, TALER_denom_sign_blinded (&ccoin->coin_sig,
&new_dkp[cnt]->priv, &new_dkp[cnt]->priv,
true, true,
bp)); bp));
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_coin_ev_hash (bp, TALER_coin_ev_hash (bp,
&cbc.denom_pub_hash, &cbc.denom_pub_hash,
&cbc.h_coin_envelope)); &cbc.h_coin_envelope));
GNUNET_assert ( GNUNET_assert (
GNUNET_OK == GNUNET_OK ==
TALER_denom_sign_blinded ( TALER_denom_sign_blinded (
&cbc.sig, &cbc.sig,
&new_dkp[cnt]->priv, &new_dkp[cnt]->priv,
false, false,
bp)); bp));
} }
} }
perm = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_NONCE, perm = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_NONCE,
NUM_ROWS); NUM_ROWS);
//BEGIN
FAILIF (GNUNET_OK != FAILIF (GNUNET_OK !=
plugin->start (plugin->cls, plugin->start (plugin->cls,
"Transaction")); "Transaction"));
for (unsigned int j = 0; j < NUM_ROWS; j++) for (unsigned int j = 0; j < NUM_ROWS; j++)
{
/*** NEED TO INSERT REFRESH COMMITMENTS + ENSURECOIN ***/
union TALER_DenominationBlindingKeyP bks;
struct GNUNET_TIME_Timestamp deadline;
struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_CoinPubHashP c_hash;
unsigned int k = (unsigned int) rand () % 5;
unsigned int i = perm[j];
if (i >= ROUNDS)
i = ROUNDS; /* throw-away slot, do not keep around */
depos[i].deposit_fee = fees.deposit;
RND_BLK (&coin_pub);
RND_BLK (&c_hash);
RND_BLK (&reserve_pub);
RND_BLK (&cbc.reserve_sig);
TALER_denom_pub_hash (&new_dkp[k]->pub,
&cbc.denom_pub_hash);
deadline = GNUNET_TIME_timestamp_get ();
RND_BLK (&depos[i].coin.coin_pub);
TALER_denom_pub_hash (&new_dkp[k]->pub,
&depos[i].coin.denom_pub_hash);
GNUNET_assert (GNUNET_OK ==
TALER_denom_sig_unblind (&depos[i].coin.denom_sig,
&ccoin->coin_sig,
&bks,
&c_hash,
&alg_values,
&new_dkp[k]->pub));
RND_BLK (&depos[i].merchant_pub);
RND_BLK (&depos[i].csig);
RND_BLK (&depos[i].h_contract_terms);
RND_BLK (&depos[i].wire_salt);
depos[i].amount_with_fee = value;
depos[i].refund_deadline = deadline;
depos[i].wire_deadline = deadline;
depos[i].receiver_wire_account =
"payto://iban/DE67830654080004822650?receiver-name=Test";
TALER_merchant_wire_signature_hash (
"payto://iban/DE67830654080004822650?receiver-name=Test",
&depos[i].wire_salt,
&h_wire_wt);
cbc.reserve_pub = reserve_pub;
cbc.amount_with_fee = value;
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (CURRENCY,
&cbc.withdraw_fee));
{ {
/*** NEED TO INSERT REFRESH COMMITMENTS + ENSURECOIN ***/ bool found;
union TALER_DenominationBlindingKeyP bks; bool nonce_ok;
struct GNUNET_TIME_Timestamp deadline; bool balance_ok;
struct TALER_CoinSpendPublicKeyP coin_pub; uint64_t ruuid;
struct TALER_ReservePublicKeyP reserve_pub; struct GNUNET_TIME_Timestamp now;
struct TALER_CoinPubHashP c_hash;
unsigned int k = (unsigned int)rand()%5; now = GNUNET_TIME_timestamp_get ();
unsigned int i = perm[j]; FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
if (i >= ROUNDS) plugin->do_withdraw (plugin->cls,
i = ROUNDS; /* throw-away slot, do not keep around */ NULL,
depos[i].deposit_fee = fees.deposit; &cbc,
RND_BLK (&coin_pub); now,
RND_BLK (&c_hash); &found,
RND_BLK (&reserve_pub); &balance_ok,
RND_BLK (&cbc.reserve_sig); &nonce_ok,
TALER_denom_pub_hash (&new_dkp[k]->pub, &ruuid));
&cbc.denom_pub_hash);
deadline = GNUNET_TIME_timestamp_get ();
RND_BLK (&depos[i].coin.coin_pub);
TALER_denom_pub_hash (&new_dkp[k]->pub,
&depos[i].coin.denom_pub_hash);
GNUNET_assert (GNUNET_OK ==
TALER_denom_sig_unblind (&depos[i].coin.denom_sig,
&ccoin->coin_sig,
&bks,
&c_hash,
&alg_values,
&new_dkp[k]->pub));
RND_BLK (&depos[i].merchant_pub);
RND_BLK (&depos[i].csig);
RND_BLK (&depos[i].h_contract_terms);
RND_BLK (&depos[i].wire_salt);
depos[i].amount_with_fee = value;
depos[i].refund_deadline = deadline;
depos[i].wire_deadline = deadline;
depos[i].receiver_wire_account =
"payto://iban/DE67830654080004822650?receiver-name=Test";
TALER_merchant_wire_signature_hash (
"payto://iban/DE67830654080004822650?receiver-name=Test",
&depos[i].wire_salt,
&h_wire_wt);
cbc.reserve_pub = reserve_pub;
cbc.amount_with_fee = value;
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (CURRENCY,
&cbc.withdraw_fee));
{
/* INSERT WIRE TARGETS */
bool found;
bool nonce_ok;
bool balance_ok;
uint64_t ruuid;
struct GNUNET_TIME_Timestamp now;
now = GNUNET_TIME_timestamp_get ();
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->do_withdraw (plugin->cls,
NULL,
&cbc,
now,
&found,
&balance_ok,
&nonce_ok,
&ruuid));
}
{
/* ENSURE_COIN_KNOWN */
uint64_t known_coin_id;
struct TALER_DenominationHashP dph;
struct TALER_AgeCommitmentHash agh;
FAILIF (TALER_EXCHANGEDB_CKS_ADDED !=
plugin->ensure_coin_known (plugin->cls,
&depos[i].coin,
&known_coin_id,
&dph,
&agh));
refresh.coin = depos[i].coin;
RND_BLK (&refresh.coin_sig);
RND_BLK (&refresh.rc);
refresh.amount_with_fee = value;
refresh.noreveal_index = MELT_NOREVEAL_INDEX;
}
/*STORE INTO DEPOSIT*/
{
struct GNUNET_TIME_Timestamp now;
now = GNUNET_TIME_timestamp_get ();
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_deposit (plugin->cls,
now,
&depos[i]));
}
if (ROUNDS == i)
TALER_denom_sig_free (&depos[i].coin.denom_sig);
} }
/* End of benchmark setup */ {
GNUNET_free(perm); /* ENSURE_COIN_KNOWN */
// commit uint64_t known_coin_id;
struct TALER_DenominationHashP dph;
struct TALER_AgeCommitmentHash agh;
FAILIF (TALER_EXCHANGEDB_CKS_ADDED !=
plugin->ensure_coin_known (plugin->cls,
&depos[i].coin,
&known_coin_id,
&dph,
&agh));
refresh.coin = depos[i].coin;
RND_BLK (&refresh.coin_sig);
RND_BLK (&refresh.rc);
refresh.amount_with_fee = value;
refresh.noreveal_index = MELT_NOREVEAL_INDEX;
}
{
struct GNUNET_TIME_Timestamp now;
now = GNUNET_TIME_timestamp_get ();
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_deposit (plugin->cls,
now,
&depos[i]));
}
if (ROUNDS == i)
TALER_denom_sig_free (&depos[i].coin.denom_sig);
}
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->commit (plugin->cls)); plugin->commit (plugin->cls));
GNUNET_free (perm);
/* End of benchmark setup */
/**** CALL GET READY DEPOSIT ****/ /**** CALL GET READY DEPOSIT ****/
for (unsigned int r=0; r< ROUNDS; r++) for (unsigned int r = 0; r< ROUNDS; r++)
{ {
struct GNUNET_TIME_Absolute time; struct GNUNET_TIME_Absolute time;
struct GNUNET_TIME_Relative duration; struct GNUNET_TIME_Relative duration;
struct TALER_MerchantPublicKeyP merchant_pub; struct TALER_MerchantPublicKeyP merchant_pub;
char *payto_uri; char *payto_uri;
time = GNUNET_TIME_absolute_get(); enum GNUNET_DB_QueryStatus qs;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_ready_deposit (plugin->cls,
0,
INT32_MAX,
&merchant_pub,
&payto_uri));
time = GNUNET_TIME_absolute_get ();
qs = plugin->get_ready_deposit (plugin->cls,
0,
INT32_MAX,
&merchant_pub,
&payto_uri);
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
duration = GNUNET_TIME_absolute_get_duration (time); duration = GNUNET_TIME_absolute_get_duration (time);
times = GNUNET_TIME_relative_add (times, times = GNUNET_TIME_relative_add (times,
duration); duration);
duration_sq = duration.rel_value_us * duration.rel_value_us; duration_sq = duration.rel_value_us * duration.rel_value_us;
GNUNET_assert (duration_sq / duration.rel_value_us == duration.rel_value_us); GNUNET_assert (duration_sq / duration.rel_value_us ==
duration.rel_value_us);
GNUNET_assert (sqrs + duration_sq >= sqrs); GNUNET_assert (sqrs + duration_sq >= sqrs);
sqrs += duration_sq; sqrs += duration_sq;
} }
@ -447,15 +447,14 @@ run (void *cls)
ROUNDS); ROUNDS);
avg_dbl = avg.rel_value_us; avg_dbl = avg.rel_value_us;
variance = sqrs - (avg_dbl * avg_dbl * ROUNDS); variance = sqrs - (avg_dbl * avg_dbl * ROUNDS);
fprintf(stdout, fprintf (stdout,
"%8llu ± %6.0f\n", "%8llu ± %6.0f\n",
(unsigned long long) avg.rel_value_us, (unsigned long long) avg.rel_value_us,
sqrt (variance / (ROUNDS-1))); sqrt (variance / (ROUNDS - 1)));
} }
result = 0; result = 0;
drop: drop:
GNUNET_break (GNUNET_OK == // GNUNET_break (GNUNET_OK == plugin->drop_tables (plugin->cls));
plugin->drop_tables (plugin->cls));
cleanup: cleanup:
if (NULL != revealed_coins) if (NULL != revealed_coins)
{ {
@ -473,12 +472,12 @@ cleanup:
cnt++) cnt++)
destroy_denom_key_pair (new_dkp[cnt]); destroy_denom_key_pair (new_dkp[cnt]);
GNUNET_free (new_dkp); GNUNET_free (new_dkp);
for (unsigned int i=0; i< ROUNDS ; i++) for (unsigned int i = 0; i< ROUNDS; i++)
{ {
TALER_denom_sig_free (&depos[i].coin.denom_sig); TALER_denom_sig_free (&depos[i].coin.denom_sig);
} }
GNUNET_free(depos); GNUNET_free (depos);
GNUNET_free(ref); GNUNET_free (ref);
TALER_EXCHANGEDB_plugin_unload (plugin); TALER_EXCHANGEDB_plugin_unload (plugin);
plugin = NULL; plugin = NULL;
} }
@ -490,7 +489,6 @@ main (int argc,
{ {
const char *plugin_name; const char *plugin_name;
char *config_filename; char *config_filename;
char *testname;
struct GNUNET_CONFIGURATION_Handle *cfg; struct GNUNET_CONFIGURATION_Handle *cfg;
(void) argc; (void) argc;
@ -504,15 +502,17 @@ main (int argc,
"WARNING", "WARNING",
NULL); NULL);
plugin_name++; plugin_name++;
(void) GNUNET_asprintf (&testname, {
"test-exchange-db-%s", char *testname;
plugin_name);
(void) GNUNET_asprintf (&config_filename, GNUNET_asprintf (&testname,
"%s.conf", "test-exchange-db-%s",
testname); plugin_name);
fprintf (stdout, GNUNET_asprintf (&config_filename,
"Using config: %s\n", "%s.conf",
config_filename); testname);
GNUNET_free (testname);
}
cfg = GNUNET_CONFIGURATION_create (); cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_parse (cfg, GNUNET_CONFIGURATION_parse (cfg,
@ -520,16 +520,14 @@ main (int argc,
{ {
GNUNET_break (0); GNUNET_break (0);
GNUNET_free (config_filename); GNUNET_free (config_filename);
GNUNET_free (testname);
return 2; return 2;
} }
GNUNET_SCHEDULER_run (&run, GNUNET_SCHEDULER_run (&run,
cfg); cfg);
GNUNET_CONFIGURATION_destroy (cfg); GNUNET_CONFIGURATION_destroy (cfg);
GNUNET_free (config_filename); GNUNET_free (config_filename);
GNUNET_free (testname);
return result; return result;
} }
/* end of test_exchangedb_by_j.c */ /* end of perf_deposits_get_ready.c */

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2022 Taler Systems SA Copyright (C) 2014-2023 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 General Public License as published by the Free Software terms of the GNU General Public License as published by the Free Software
@ -14,8 +14,8 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/** /**
* @file exchangedb/test_exchangedb_populate_link_data.c * @file exchangedb/perf_get_link_data.c
* @brief test cases for DB interaction functions * @brief benchmark for get_link_data
* @author Joseph Xu * @author Joseph Xu
*/ */
#include "platform.h" #include "platform.h"
@ -29,7 +29,7 @@
*/ */
#define FAILIF(cond) \ #define FAILIF(cond) \
do { \ do { \
if (! (cond)) {break;} \ if (! (cond)) {break;} \
GNUNET_break (0); \ GNUNET_break (0); \
goto drop; \ goto drop; \
} while (0) } while (0)
@ -166,6 +166,8 @@ create_denom_key_pair (unsigned int size,
} }
return dkp; return dkp;
} }
/** /**
* Function called with the session hashes and transfer secret * Function called with the session hashes and transfer secret
* information for a given coin. * information for a given coin.
@ -199,22 +201,22 @@ run (void *cls)
struct GNUNET_CONFIGURATION_Handle *cfg = cls; struct GNUNET_CONFIGURATION_Handle *cfg = cls;
const uint32_t num_partitions = 10; const uint32_t num_partitions = 10;
struct DenomKeyPair *dkp = NULL; struct DenomKeyPair *dkp = NULL;
struct TALER_EXCHANGEDB_Deposit *depos=NULL; struct TALER_EXCHANGEDB_Deposit *depos = NULL;
struct TALER_Amount value; struct TALER_Amount value;
struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO; struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO;
unsigned long long sqrs=0; unsigned long long sqrs = 0;
struct TALER_EXCHANGEDB_Refund *ref=NULL; struct TALER_EXCHANGEDB_Refund *ref = NULL;
unsigned int *perm; unsigned int *perm;
unsigned long long duration_sq; unsigned long long duration_sq;
struct TALER_ExchangeWithdrawValues alg_values = { struct TALER_ExchangeWithdrawValues alg_values = {
.cipher = TALER_DENOMINATION_RSA .cipher = TALER_DENOMINATION_RSA
}; };
ref = GNUNET_new_array (ROUNDS +1, ref = GNUNET_new_array (ROUNDS + 1,
struct TALER_EXCHANGEDB_Refund); struct TALER_EXCHANGEDB_Refund);
depos = GNUNET_new_array (ROUNDS +1, depos = GNUNET_new_array (ROUNDS + 1,
struct TALER_EXCHANGEDB_Deposit); struct TALER_EXCHANGEDB_Deposit);
refresh = GNUNET_new_array (ROUNDS +1, refresh = GNUNET_new_array (ROUNDS + 1,
struct TALER_EXCHANGEDB_Refresh); struct TALER_EXCHANGEDB_Refresh);
if (NULL == if (NULL ==
@ -256,16 +258,14 @@ run (void *cls)
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":0.000010", TALER_string_to_amount (CURRENCY ":0.000010",
&fees.refund)); &fees.refund));
//DENOMINATION
{ {
//PAIR KEY LIST
new_dkp = GNUNET_new_array (MELT_NEW_COINS, new_dkp = GNUNET_new_array (MELT_NEW_COINS,
struct DenomKeyPair *); struct DenomKeyPair *);
for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++) for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++)
{ {
struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE, new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE,
now, now,
&value, &value,
@ -275,7 +275,6 @@ run (void *cls)
} }
perm = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_NONCE, perm = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_NONCE,
NUM_ROWS); NUM_ROWS);
//BEGIN
FAILIF (GNUNET_OK != FAILIF (GNUNET_OK !=
plugin->start (plugin->cls, plugin->start (plugin->cls,
"Transaction")); "Transaction"));
@ -290,89 +289,96 @@ run (void *cls)
i = ROUNDS; /* throw-away slot, do not keep around */ i = ROUNDS; /* throw-away slot, do not keep around */
RND_BLK (&depos[i].coin.coin_pub); RND_BLK (&depos[i].coin.coin_pub);
ZR_BLK (&cbc); ZR_BLK (&cbc);
TALER_denom_pub_hash (&new_dkp[(unsigned int)rand()%MELT_NEW_COINS]->pub, TALER_denom_pub_hash (&new_dkp[(unsigned int) rand ()
% MELT_NEW_COINS]->pub,
&depos[i].coin.denom_pub_hash); &depos[i].coin.denom_pub_hash);
{ {
struct TALER_EXCHANGEDB_RefreshRevealedCoin revealed_coins[MELT_NEW_COINS]; struct TALER_EXCHANGEDB_RefreshRevealedCoin
revealed_coins[MELT_NEW_COINS];
for (unsigned int p=0;p<MELT_NEW_COINS;p++)
{
struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coin = &revealed_coins[p];
struct TALER_BlindedPlanchet *bp = &revealed_coin->blinded_planchet;
struct TALER_BlindedRsaPlanchet *rp = &bp->details.rsa_blinded_planchet;
/* h_coin_ev must be unique, but we only have MELT_NEW_COINS created
above for NUM_ROWS iterations; instead of making "all new" coins,
we simply randomize the hash here as nobody is checking for consistency
anyway ;-) */
bp->cipher = TALER_DENOMINATION_RSA;
rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 (
GNUNET_CRYPTO_QUALITY_WEAK,
(RSA_KEY_SIZE / 8) - 1);
rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size);
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
rp->blinded_msg,
rp->blinded_msg_size);
TALER_denom_pub_hash (&new_dkp[(unsigned int)rand()%MELT_NEW_COINS]->pub,
&revealed_coin->h_denom_pub);
revealed_coin->exchange_vals = alg_values;
TALER_coin_ev_hash (bp,
&revealed_coin->h_denom_pub,
&revealed_coin->coin_envelope_hash);
GNUNET_assert (GNUNET_OK ==
TALER_denom_sign_blinded (&revealed_coin->coin_sig,
&new_dkp[(unsigned int)rand()%MELT_NEW_COINS]->priv,
true,
bp));
GNUNET_assert (
GNUNET_OK ==
TALER_denom_sign_blinded (
&cbc.sig,
&new_dkp[(unsigned int)rand()%MELT_NEW_COINS]->priv,
false,
bp));
}
GNUNET_assert (GNUNET_OK ==
TALER_denom_sig_unblind (&depos[i].coin.denom_sig,
&cbc.sig,
&bks,
&c_hash,
&alg_values,
&new_dkp[(unsigned int)rand()%MELT_NEW_COINS]->pub));
{
/* ENSURE_COIN_KNOWN */
struct TALER_DenominationHashP dph;
struct TALER_AgeCommitmentHash agh;
bool zombie_required = false;
bool balance_ok;
FAILIF (TALER_EXCHANGEDB_CKS_ADDED != for (unsigned int p = 0; p<MELT_NEW_COINS; p++)
plugin->ensure_coin_known (plugin->cls, {
&depos[i].coin, struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coin =
&known_coin_id, &revealed_coins[p];
&dph, struct TALER_BlindedPlanchet *bp = &revealed_coin->blinded_planchet;
&agh)); struct TALER_BlindedRsaPlanchet *rp = &bp->details.rsa_blinded_planchet;
/**** INSERT REFRESH COMMITMENTS ****/
refresh[i].coin = depos[i].coin; /* h_coin_ev must be unique, but we only have MELT_NEW_COINS created
RND_BLK (&refresh[i].coin_sig); above for NUM_ROWS iterations; instead of making "all new" coins,
RND_BLK (&refresh[i].rc); we simply randomize the hash here as nobody is checking for consistency
refresh[i].amount_with_fee = value; anyway ;-) */
refresh[i].noreveal_index = MELT_NOREVEAL_INDEX; bp->cipher = TALER_DENOMINATION_RSA;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 (
plugin->do_melt (plugin->cls, GNUNET_CRYPTO_QUALITY_WEAK,
NULL, (RSA_KEY_SIZE / 8) - 1);
&refresh[i], rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size);
known_coin_id, GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&zombie_required, rp->blinded_msg,
&balance_ok)); rp->blinded_msg_size);
} TALER_denom_pub_hash (&new_dkp[(unsigned int) rand ()
% MELT_NEW_COINS]->pub,
&revealed_coin->h_denom_pub);
revealed_coin->exchange_vals = alg_values;
TALER_coin_ev_hash (bp,
&revealed_coin->h_denom_pub,
&revealed_coin->coin_envelope_hash);
GNUNET_assert (GNUNET_OK ==
TALER_denom_sign_blinded (&revealed_coin->coin_sig,
&new_dkp[(unsigned
int) rand ()
% MELT_NEW_COINS]->
priv,
true,
bp));
GNUNET_assert (
GNUNET_OK ==
TALER_denom_sign_blinded (
&cbc.sig,
&new_dkp[(unsigned int) rand () % MELT_NEW_COINS]->priv,
false,
bp));
}
GNUNET_assert (GNUNET_OK ==
TALER_denom_sig_unblind (&depos[i].coin.denom_sig,
&cbc.sig,
&bks,
&c_hash,
&alg_values,
&new_dkp[(unsigned int) rand ()
% MELT_NEW_COINS]->pub));
{
/* ENSURE_COIN_KNOWN */
struct TALER_DenominationHashP dph;
struct TALER_AgeCommitmentHash agh;
bool zombie_required = false;
bool balance_ok;
FAILIF (TALER_EXCHANGEDB_CKS_ADDED !=
plugin->ensure_coin_known (plugin->cls,
&depos[i].coin,
&known_coin_id,
&dph,
&agh));
/**** INSERT REFRESH COMMITMENTS ****/
refresh[i].coin = depos[i].coin;
RND_BLK (&refresh[i].coin_sig);
RND_BLK (&refresh[i].rc);
refresh[i].amount_with_fee = value;
refresh[i].noreveal_index = MELT_NOREVEAL_INDEX;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->do_melt (plugin->cls,
NULL,
&refresh[i],
known_coin_id,
&zombie_required,
&balance_ok));
}
/****GET melt_serial_id generated by default****/ /****GET melt_serial_id generated by default****/
{ {
struct TALER_EXCHANGEDB_Melt ret_refresh_session; struct TALER_EXCHANGEDB_Melt ret_refresh_session;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_melt (plugin->cls, plugin->get_melt (plugin->cls,
&refresh[i].rc, &refresh[i].rc,
@ -382,7 +388,7 @@ run (void *cls)
/**** INSERT REFRESH_REVEAL + TRANSFER_KEYS *****/ /**** INSERT REFRESH_REVEAL + TRANSFER_KEYS *****/
{ {
static unsigned int cnt; static unsigned int cnt;
RND_BLK (&tprivs); RND_BLK (&tprivs);
RND_BLK (&tpub); RND_BLK (&tpub);
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
@ -398,14 +404,14 @@ run (void *cls)
} }
for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++) for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++)
{ {
TALER_blinded_denom_sig_free (&revealed_coins[cnt].coin_sig); TALER_blinded_denom_sig_free (&revealed_coins[cnt].coin_sig);
TALER_blinded_planchet_free (&revealed_coins[cnt].blinded_planchet); TALER_blinded_planchet_free (&revealed_coins[cnt].blinded_planchet);
} }
/* { /* {
struct TALER_CoinSpendPublicKeyP ocp; struct TALER_CoinSpendPublicKeyP ocp;
uint64_t rrc_serial; uint64_t rrc_serial;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_old_coin_by_h_blind (plugin->cls, plugin->get_old_coin_by_h_blind (plugin->cls,
&revealed_coins->coin_envelope_hash, &revealed_coins->coin_envelope_hash,
@ -417,17 +423,15 @@ run (void *cls)
TALER_denom_sig_free (&depos[i].coin.denom_sig); TALER_denom_sig_free (&depos[i].coin.denom_sig);
} }
/* End of benchmark setup */ /* End of benchmark setup */
GNUNET_free(perm); GNUNET_free (perm);
// commit
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->commit (plugin->cls)); plugin->commit (plugin->cls));
/**** CALL GET LINK DATA ****/ for (unsigned int r = 0; r< ROUNDS; r++)
for (unsigned int r=0; r< ROUNDS; r++)
{ {
struct GNUNET_TIME_Absolute time; struct GNUNET_TIME_Absolute time;
struct GNUNET_TIME_Relative duration; struct GNUNET_TIME_Relative duration;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
time = GNUNET_TIME_absolute_get(); time = GNUNET_TIME_absolute_get ();
qs = plugin->get_link_data (plugin->cls, qs = plugin->get_link_data (plugin->cls,
&refresh[r].coin.coin_pub, &refresh[r].coin.coin_pub,
@ -439,7 +443,8 @@ run (void *cls)
times = GNUNET_TIME_relative_add (times, times = GNUNET_TIME_relative_add (times,
duration); duration);
duration_sq = duration.rel_value_us * duration.rel_value_us; duration_sq = duration.rel_value_us * duration.rel_value_us;
GNUNET_assert (duration_sq / duration.rel_value_us == duration.rel_value_us); GNUNET_assert (duration_sq / duration.rel_value_us ==
duration.rel_value_us);
GNUNET_assert (sqrs + duration_sq >= sqrs); GNUNET_assert (sqrs + duration_sq >= sqrs);
sqrs += duration_sq; sqrs += duration_sq;
} }
@ -454,10 +459,10 @@ run (void *cls)
ROUNDS); ROUNDS);
avg_dbl = avg.rel_value_us; avg_dbl = avg.rel_value_us;
variance = sqrs - (avg_dbl * avg_dbl * ROUNDS); variance = sqrs - (avg_dbl * avg_dbl * ROUNDS);
fprintf(stdout, fprintf (stdout,
"%8llu ± %6.0f\n", "%8llu ± %6.0f\n",
(unsigned long long) avg.rel_value_us, (unsigned long long) avg.rel_value_us,
sqrt (variance / (ROUNDS-1))); sqrt (variance / (ROUNDS - 1)));
} }
result = 0; result = 0;
drop: drop:
@ -470,13 +475,13 @@ cleanup:
cnt++) cnt++)
destroy_denom_key_pair (new_dkp[cnt]); destroy_denom_key_pair (new_dkp[cnt]);
GNUNET_free (new_dkp); GNUNET_free (new_dkp);
for (unsigned int i=0; i< ROUNDS; i++) for (unsigned int i = 0; i< ROUNDS; i++)
{ {
TALER_denom_sig_free (&depos[i].coin.denom_sig); TALER_denom_sig_free (&depos[i].coin.denom_sig);
} }
GNUNET_free(depos); GNUNET_free (depos);
GNUNET_free(ref); GNUNET_free (ref);
GNUNET_free(refresh); GNUNET_free (refresh);
dkp = NULL; dkp = NULL;
TALER_EXCHANGEDB_plugin_unload (plugin); TALER_EXCHANGEDB_plugin_unload (plugin);
plugin = NULL; plugin = NULL;
@ -509,9 +514,6 @@ main (int argc,
(void) GNUNET_asprintf (&config_filename, (void) GNUNET_asprintf (&config_filename,
"%s.conf", "%s.conf",
testname); testname);
fprintf (stdout,
"Using config: %s\n",
config_filename);
cfg = GNUNET_CONFIGURATION_create (); cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_parse (cfg, GNUNET_CONFIGURATION_parse (cfg,

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2022 Taler Systems SA Copyright (C) 2014-2023 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 General Public License as published by the Free Software terms of the GNU General Public License as published by the Free Software
@ -14,8 +14,8 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/** /**
* @file exchangedb/test_exchangedb_by_j.c * @file exchangedb/perf_reserves_in_insert.c
* @brief test cases for DB interaction functions * @brief benchmark for 'reserves_in_insert'
* @author Joseph Xu * @author Joseph Xu
*/ */
#include "platform.h" #include "platform.h"
@ -23,6 +23,7 @@
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h" #include "taler_exchangedb_plugin.h"
/** /**
* Global result from the testcase. * Global result from the testcase.
*/ */
@ -33,7 +34,7 @@ static int result;
*/ */
#define FAILIF(cond) \ #define FAILIF(cond) \
do { \ do { \
if (! (cond)) { break;} \ if (! (cond)) {break;} \
GNUNET_break (0); \ GNUNET_break (0); \
goto drop; \ goto drop; \
} while (0) } while (0)
@ -51,6 +52,10 @@ static int result;
#define ZR_BLK(ptr) \ #define ZR_BLK(ptr) \
memset (ptr, 0, sizeof (*ptr)) memset (ptr, 0, sizeof (*ptr))
/**
* How many rounds do we average over?
*/
#define ROUNDS 5
/** /**
* Currency we use. Must match test-exchange-db-*.conf. * Currency we use. Must match test-exchange-db-*.conf.
@ -73,6 +78,10 @@ run (void *cls)
{ {
struct GNUNET_CONFIGURATION_Handle *cfg = cls; struct GNUNET_CONFIGURATION_Handle *cfg = cls;
const uint32_t num_partitions = 10; const uint32_t num_partitions = 10;
static unsigned int batches[] = {1, 1, 2, 3, 4, 16, 32 };
const unsigned int lcm = 3 * 32;
struct GNUNET_TIME_Relative times[sizeof (batches) / sizeof(*batches)];
unsigned long long sqrs[sizeof (batches) / sizeof(*batches)];
if (NULL == if (NULL ==
(plugin = TALER_EXCHANGEDB_plugin_load (cfg))) (plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
@ -91,46 +100,78 @@ run (void *cls)
result = 77; result = 77;
goto cleanup; goto cleanup;
} }
for (unsigned int i = 0; i< 8; i++)
{
static unsigned int batches[] = {1, 1, 0, 2, 4, 16, 64, 256};
const char *sndr = "payto://x-taler-bank/localhost:8080/1";
struct TALER_Amount value;
unsigned int batch_size = batches[i];
struct GNUNET_TIME_Absolute now;
struct GNUNET_TIME_Timestamp ts;
struct GNUNET_TIME_Relative duration;
struct TALER_ReservePublicKeyP reserve_pub;
GNUNET_assert (GNUNET_OK == memset (times, 0, sizeof (times));
TALER_string_to_amount (CURRENCY ":1.000010", memset (sqrs, 0, sizeof (sqrs));
&value)); for (unsigned int r = 0; r < ROUNDS; r++)
now = GNUNET_TIME_absolute_get (); {
ts = GNUNET_TIME_timestamp_get (); for (unsigned int i = 0;
for (unsigned int r = 0; r<10; r++) i< sizeof(batches) / sizeof(*batches);
i++)
{ {
plugin->start (plugin->cls, unsigned int batch_size = batches[i];
"test_by_exchange_j"); struct TALER_Amount value;
for (unsigned int k = 0; k<batch_size; k++) struct GNUNET_TIME_Absolute now;
struct GNUNET_TIME_Timestamp ts;
unsigned long long duration_sq;
struct GNUNET_TIME_Relative duration;
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":1.000010",
&value));
now = GNUNET_TIME_absolute_get ();
ts = GNUNET_TIME_timestamp_get ();
{ {
RND_BLK (&reserve_pub); const char *sndr = "payto://x-taler-bank/localhost:8080/1";
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != struct TALER_ReservePublicKeyP reserve_pubs[lcm];
struct TALER_EXCHANGEDB_ReserveInInfo reserves[lcm];
enum GNUNET_DB_QueryStatus results[lcm];
for (unsigned int k = 0; k<lcm; k++)
{
RND_BLK (&reserve_pubs[k]);
reserves[k].reserve_pub = &reserve_pubs[k];
reserves[k].balance = &value;
reserves[k].execution_time = ts;
reserves[k].sender_account_details = sndr;
reserves[k].exchange_account_name = "name";
reserves[k].wire_reference = k;
}
FAILIF (lcm !=
plugin->reserves_in_insert (plugin->cls, plugin->reserves_in_insert (plugin->cls,
&reserve_pub, reserves,
&value, lcm,
ts, batch_size,
sndr, results));
"section",
4));
} }
plugin->commit (plugin->cls); duration = GNUNET_TIME_absolute_get_duration (now);
} times[i] = GNUNET_TIME_relative_add (times[i],
duration = GNUNET_TIME_absolute_get_duration (now); duration);
duration_sq = duration.rel_value_us * duration.rel_value_us;
GNUNET_assert (duration_sq / duration.rel_value_us ==
duration.rel_value_us);
GNUNET_assert (sqrs[i] + duration_sq >= sqrs[i]);
sqrs[i] += duration_sq;
} /* for 'i' batch size */
} /* for 'r' ROUNDS */
for (unsigned int i = 0;
i< sizeof(batches) / sizeof(*batches);
i++)
{
struct GNUNET_TIME_Relative avg;
double avg_dbl;
double variance;
avg = GNUNET_TIME_relative_divide (times[i],
ROUNDS);
avg_dbl = avg.rel_value_us;
variance = sqrs[i] - (avg_dbl * avg_dbl * ROUNDS);
fprintf (stdout, fprintf (stdout,
"for a batchsize equal to %d it took %s\n", "Batch[%2u]: %8llu ± %6.0f\n",
batch_size, batches[i],
GNUNET_STRINGS_relative_time_to_string (duration, (unsigned long long) avg.rel_value_us,
GNUNET_NO) ); sqrt (variance / (ROUNDS - 1)));
} }
result = 0; result = 0;
drop: drop:
@ -150,7 +191,6 @@ main (int argc,
char *config_filename; char *config_filename;
char *testname; char *testname;
struct GNUNET_CONFIGURATION_Handle *cfg; struct GNUNET_CONFIGURATION_Handle *cfg;
(void) argc; (void) argc;
result = -1; result = -1;
if (NULL == (plugin_name = strrchr (argv[0], (int) '-'))) if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
@ -158,6 +198,7 @@ main (int argc,
GNUNET_break (0); GNUNET_break (0);
return -1; return -1;
} }
GNUNET_log_setup (argv[0], GNUNET_log_setup (argv[0],
"WARNING", "WARNING",
NULL); NULL);
@ -168,9 +209,6 @@ main (int argc,
(void) GNUNET_asprintf (&config_filename, (void) GNUNET_asprintf (&config_filename,
"%s.conf", "%s.conf",
testname); testname);
fprintf (stdout,
"Using config: %s\n",
config_filename);
cfg = GNUNET_CONFIGURATION_create (); cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_parse (cfg, GNUNET_CONFIGURATION_parse (cfg,
@ -190,4 +228,4 @@ main (int argc,
} }
/* end of test_exchangedb_by_j.c */ /* end of perf_reserves_in_insert.c */

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2022 Taler Systems SA Copyright (C) 2014-2023 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 General Public License as published by the Free Software terms of the GNU General Public License as published by the Free Software
@ -14,8 +14,8 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/** /**
* @file exchangedb/test_exchangedb_populate_table.c * @file exchangedb/perf_select_refunds_by_coin.c
* @brief test cases for DB interaction functions * @brief benchmark for select_refunds_by_coin
* @author Joseph Xu * @author Joseph Xu
*/ */
#include "platform.h" #include "platform.h"
@ -34,7 +34,7 @@ static int result;
*/ */
#define FAILIF(cond) \ #define FAILIF(cond) \
do { \ do { \
if (! (cond)) {break;} \ if (! (cond)) {break;} \
GNUNET_break (0); \ GNUNET_break (0); \
goto drop; \ goto drop; \
} while (0) } while (0)
@ -56,24 +56,31 @@ static int result;
*/ */
#define CURRENCY "EUR" #define CURRENCY "EUR"
#define RSA_KEY_SIZE 1024 #define RSA_KEY_SIZE 1024
#define ROUNDS 10000 #define ROUNDS 100
#define NUM_ROWS 1000000 #define NUM_ROWS 1000
#define MELT_NEW_COINS 5 #define MELT_NEW_COINS 5
#define MELT_NOREVEAL_INDEX 1 #define MELT_NOREVEAL_INDEX 1
/** /**
* Database plugin under test. * Database plugin under test.
*/ */
static struct TALER_EXCHANGEDB_Plugin *plugin; static struct TALER_EXCHANGEDB_Plugin *plugin;
static struct TALER_DenomFeeSet fees; static struct TALER_DenomFeeSet fees;
static struct TALER_MerchantWireHashP h_wire_wt; static struct TALER_MerchantWireHashP h_wire_wt;
static struct DenomKeyPair **new_dkp; static struct DenomKeyPair **new_dkp;
static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins; static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins;
struct DenomKeyPair struct DenomKeyPair
{ {
struct TALER_DenominationPrivateKey priv; struct TALER_DenominationPrivateKey priv;
struct TALER_DenominationPublicKey pub; struct TALER_DenominationPublicKey pub;
}; };
/** /**
* Destroy a denomination key pair. The key is not necessarily removed from the DB. * Destroy a denomination key pair. The key is not necessarily removed from the DB.
* *
@ -166,6 +173,8 @@ create_denom_key_pair (unsigned int size,
} }
return dkp; return dkp;
} }
/** /**
* Callback invoked with information about refunds applicable * Callback invoked with information about refunds applicable
* to a particular coin. * to a particular coin.
@ -179,6 +188,7 @@ check_refund_cb (void *cls,
const struct TALER_Amount *amount_with_fee) const struct TALER_Amount *amount_with_fee)
{ {
const struct TALER_EXCHANGEDB_Refund *refund = cls; const struct TALER_EXCHANGEDB_Refund *refund = cls;
if (0 != TALER_amount_cmp (amount_with_fee, if (0 != TALER_amount_cmp (amount_with_fee,
&refund->details.refund_amount)) &refund->details.refund_amount))
{ {
@ -201,7 +211,7 @@ run (void *cls)
struct GNUNET_CONFIGURATION_Handle *cfg = cls; struct GNUNET_CONFIGURATION_Handle *cfg = cls;
const uint32_t num_partitions = 10; const uint32_t num_partitions = 10;
struct GNUNET_TIME_Timestamp ts; struct GNUNET_TIME_Timestamp ts;
struct TALER_EXCHANGEDB_Deposit *depos=NULL; struct TALER_EXCHANGEDB_Deposit *depos = NULL;
struct GNUNET_TIME_Timestamp deadline; struct GNUNET_TIME_Timestamp deadline;
struct TALER_Amount value; struct TALER_Amount value;
union TALER_DenominationBlindingKeyP bks; union TALER_DenominationBlindingKeyP bks;
@ -209,20 +219,20 @@ run (void *cls)
struct TALER_EXCHANGEDB_CollectableBlindcoin cbc; struct TALER_EXCHANGEDB_CollectableBlindcoin cbc;
struct TALER_ExchangeWithdrawValues alg_values = { struct TALER_ExchangeWithdrawValues alg_values = {
.cipher = TALER_DENOMINATION_RSA .cipher = TALER_DENOMINATION_RSA
}; };
struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO; struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO;
unsigned long long sqrs = 0; unsigned long long sqrs = 0;
struct TALER_EXCHANGEDB_Refund *ref=NULL; struct TALER_EXCHANGEDB_Refund *ref = NULL;
unsigned int *perm; unsigned int *perm;
unsigned long long duration_sq; unsigned long long duration_sq;
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin; struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin;
struct TALER_DenominationPublicKey *new_denom_pubs = NULL; struct TALER_DenominationPublicKey *new_denom_pubs = NULL;
unsigned int count=0; unsigned int count = 0;
ref = GNUNET_new_array (ROUNDS +1, ref = GNUNET_new_array (ROUNDS + 1,
struct TALER_EXCHANGEDB_Refund); struct TALER_EXCHANGEDB_Refund);
depos = GNUNET_new_array (ROUNDS +1, depos = GNUNET_new_array (ROUNDS + 1,
struct TALER_EXCHANGEDB_Deposit); struct TALER_EXCHANGEDB_Deposit);
ZR_BLK (&cbc); ZR_BLK (&cbc);
@ -249,8 +259,6 @@ run (void *cls)
GNUNET_break (0); GNUNET_break (0);
goto cleanup; goto cleanup;
} }
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":1.000010", TALER_string_to_amount (CURRENCY ":1.000010",
&value)); &value));
@ -270,78 +278,72 @@ run (void *cls)
ts = GNUNET_TIME_timestamp_get (); ts = GNUNET_TIME_timestamp_get ();
deadline = GNUNET_TIME_timestamp_get (); deadline = GNUNET_TIME_timestamp_get ();
//DENOMINATION
{ {
//PAIR KEY LIST
new_dkp = GNUNET_new_array (MELT_NEW_COINS, new_dkp = GNUNET_new_array (MELT_NEW_COINS,
struct DenomKeyPair *); struct DenomKeyPair *);
//PUBLIC KEY LIST
new_denom_pubs = GNUNET_new_array (MELT_NEW_COINS, new_denom_pubs = GNUNET_new_array (MELT_NEW_COINS,
struct TALER_DenominationPublicKey); struct TALER_DenominationPublicKey);
//REFRESH REVEAL COIN LIST
revealed_coins revealed_coins
= GNUNET_new_array (MELT_NEW_COINS, = GNUNET_new_array (MELT_NEW_COINS,
struct TALER_EXCHANGEDB_RefreshRevealedCoin); struct TALER_EXCHANGEDB_RefreshRevealedCoin);
for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++) for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++)
{ {
struct GNUNET_TIME_Timestamp now; struct GNUNET_TIME_Timestamp now;
struct TALER_BlindedRsaPlanchet *rp; struct TALER_BlindedRsaPlanchet *rp;
struct TALER_BlindedPlanchet *bp; struct TALER_BlindedPlanchet *bp;
now = GNUNET_TIME_timestamp_get (); now = GNUNET_TIME_timestamp_get ();
//5 KEY PAIR new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE,
new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE, now,
now, &value,
&value, &fees);
&fees); GNUNET_assert (NULL != new_dkp[cnt]);
GNUNET_assert (NULL != new_dkp[cnt]); new_denom_pubs[cnt] = new_dkp[cnt]->pub;
new_denom_pubs[cnt] = new_dkp[cnt]->pub; ccoin = &revealed_coins[cnt];
ccoin = &revealed_coins[cnt]; bp = &ccoin->blinded_planchet;
bp = &ccoin->blinded_planchet; bp->cipher = TALER_DENOMINATION_RSA;
bp->cipher = TALER_DENOMINATION_RSA; rp = &bp->details.rsa_blinded_planchet;
rp = &bp->details.rsa_blinded_planchet; rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 (
rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 ( GNUNET_CRYPTO_QUALITY_WEAK,
GNUNET_CRYPTO_QUALITY_WEAK, (RSA_KEY_SIZE / 8) - 1);
(RSA_KEY_SIZE / 8) - 1); rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size);
rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, rp->blinded_msg,
rp->blinded_msg, rp->blinded_msg_size);
rp->blinded_msg_size); TALER_denom_pub_hash (&new_dkp[cnt]->pub,
TALER_denom_pub_hash (&new_dkp[cnt]->pub, &ccoin->h_denom_pub);
&ccoin->h_denom_pub); ccoin->exchange_vals = alg_values;
ccoin->exchange_vals = alg_values; TALER_coin_ev_hash (bp,
TALER_coin_ev_hash (bp, &ccoin->h_denom_pub,
&ccoin->h_denom_pub, &ccoin->coin_envelope_hash);
&ccoin->coin_envelope_hash); GNUNET_assert (GNUNET_OK ==
GNUNET_assert (GNUNET_OK ==
TALER_denom_sign_blinded (&ccoin->coin_sig, TALER_denom_sign_blinded (&ccoin->coin_sig,
&new_dkp[cnt]->priv, &new_dkp[cnt]->priv,
true, true,
bp)); bp));
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_coin_ev_hash (bp, TALER_coin_ev_hash (bp,
&cbc.denom_pub_hash, &cbc.denom_pub_hash,
&cbc.h_coin_envelope)); &cbc.h_coin_envelope));
GNUNET_assert ( GNUNET_assert (
GNUNET_OK == GNUNET_OK ==
TALER_denom_sign_blinded ( TALER_denom_sign_blinded (
&cbc.sig, &cbc.sig,
&new_dkp[cnt]->priv, &new_dkp[cnt]->priv,
false, false,
bp)); bp));
} }
} }
perm = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_NONCE, perm = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_NONCE,
NUM_ROWS); NUM_ROWS);
// begin
FAILIF (GNUNET_OK != FAILIF (GNUNET_OK !=
plugin->start (plugin->cls, plugin->start (plugin->cls,
"Transaction")); "Transaction"));
for (unsigned int j=0; j< NUM_ROWS; j++) for (unsigned int j = 0; j< NUM_ROWS; j++)
{ {
unsigned int i = perm[j]; unsigned int i = perm[j];
unsigned int k = (unsigned int)rand()%5; unsigned int k = (unsigned int) rand () % 5;
if (i >= ROUNDS) if (i >= ROUNDS)
i = ROUNDS; /* throw-away slot, do not keep around */ i = ROUNDS; /* throw-away slot, do not keep around */
RND_BLK (&coin_pub); RND_BLK (&coin_pub);
@ -350,8 +352,6 @@ run (void *cls)
RND_BLK (&depos[i].coin.coin_pub); RND_BLK (&depos[i].coin.coin_pub);
TALER_denom_pub_hash (&new_dkp[k]->pub, TALER_denom_pub_hash (&new_dkp[k]->pub,
&depos[i].coin.denom_pub_hash); &depos[i].coin.denom_pub_hash);
// TALER_denom_pub_hash (&dkp->pub,
// &ref.coin.denom_pub_hash);
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_denom_sig_unblind (&depos[i].coin.denom_sig, TALER_denom_sig_unblind (&depos[i].coin.denom_sig,
&cbc.sig, &cbc.sig,
@ -369,15 +369,15 @@ run (void *cls)
depos[i].receiver_wire_account = depos[i].receiver_wire_account =
"payto://iban/DE67830654080004822650?receiver-name=Test"; "payto://iban/DE67830654080004822650?receiver-name=Test";
TALER_merchant_wire_signature_hash ( TALER_merchant_wire_signature_hash (
"payto://iban/DE67830654080004822650?receiver-name=Test", "payto://iban/DE67830654080004822650?receiver-name=Test",
&depos[i].wire_salt, &depos[i].wire_salt,
&h_wire_wt); &h_wire_wt);
depos[i].timestamp = ts; depos[i].timestamp = ts;
uint64_t known_coin_id; uint64_t known_coin_id;
{//ENSURE_COIN_KNOWN {
struct TALER_DenominationHashP dph; struct TALER_DenominationHashP dph;
struct TALER_AgeCommitmentHash agh; struct TALER_AgeCommitmentHash agh;
FAILIF (TALER_EXCHANGEDB_CKS_ADDED != FAILIF (TALER_EXCHANGEDB_CKS_ADDED !=
plugin->ensure_coin_known (plugin->cls, plugin->ensure_coin_known (plugin->cls,
&depos[i].coin, &depos[i].coin,
@ -385,9 +385,9 @@ run (void *cls)
&dph, &dph,
&agh)); &agh));
} }
/*STORE INTO DEPOSIT*/
{ {
struct GNUNET_TIME_Timestamp now; struct GNUNET_TIME_Timestamp now;
now = GNUNET_TIME_timestamp_get (); now = GNUNET_TIME_timestamp_get ();
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_deposit (plugin->cls, plugin->insert_deposit (plugin->cls,
@ -399,12 +399,13 @@ run (void *cls)
bool refund_ok; bool refund_ok;
bool gone; bool gone;
bool conflict; bool conflict;
unsigned int refund_percent=0; unsigned int refund_percent = 0;
switch (refund_percent){ switch (refund_percent)
case 2 ://100% refund {
case 2: // 100% refund
ref[i].coin = depos[i].coin; ref[i].coin = depos[i].coin;
ref[i].details.merchant_pub = depos[i].merchant_pub; ref[i].details.merchant_pub = depos[i].merchant_pub;
RND_BLK(&ref[i].details.merchant_sig); RND_BLK (&ref[i].details.merchant_sig);
ref[i].details.h_contract_terms = depos[i].h_contract_terms; ref[i].details.h_contract_terms = depos[i].h_contract_terms;
ref[i].coin.coin_pub = depos[i].coin.coin_pub; ref[i].coin.coin_pub = depos[i].coin.coin_pub;
ref[i].details.rtransaction_id = i; ref[i].details.rtransaction_id = i;
@ -420,58 +421,58 @@ run (void *cls)
&gone, &gone,
&conflict)); &conflict));
break; break;
case 1 ://10% refund case 1:// 10% refund
if (count < (NUM_ROWS/10)) if (count < (NUM_ROWS / 10))
{ {
ref[i].coin = depos[i].coin;
ref[i].details.merchant_pub = depos[i].merchant_pub;
RND_BLK (&ref[i].details.merchant_sig);
ref[i].details.h_contract_terms = depos[i].h_contract_terms;
ref[i].coin.coin_pub = depos[i].coin.coin_pub;
ref[i].details.rtransaction_id = i;
ref[i].details.refund_amount = value;
ref[i].details.refund_fee = fees.refund;
}
else
{
ref[i].coin = depos[i].coin;
RND_BLK (&ref[i].details.merchant_pub);
RND_BLK (&ref[i].details.merchant_sig);
RND_BLK (&ref[i].details.h_contract_terms);
RND_BLK (&ref[i].coin.coin_pub);
ref[i].details.rtransaction_id = i;
ref[i].details.refund_amount = value;
ref[i].details.refund_fee = fees.refund;
}
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->do_refund (plugin->cls,
&ref[i],
&fees.deposit,
known_coin_id,
&not_found,
&refund_ok,
&gone,
&conflict));
count++;
break;
case 0:// no refund
ref[i].coin = depos[i].coin; ref[i].coin = depos[i].coin;
ref[i].details.merchant_pub = depos[i].merchant_pub; RND_BLK (&ref[i].details.merchant_pub);
RND_BLK(&ref[i].details.merchant_sig); RND_BLK (&ref[i].details.merchant_sig);
ref[i].details.h_contract_terms = depos[i].h_contract_terms; RND_BLK (&ref[i].details.h_contract_terms);
ref[i].coin.coin_pub = depos[i].coin.coin_pub; RND_BLK (&ref[i].coin.coin_pub);
ref[i].details.rtransaction_id = i; ref[i].details.rtransaction_id = i;
ref[i].details.refund_amount = value; ref[i].details.refund_amount = value;
ref[i].details.refund_fee = fees.refund; ref[i].details.refund_fee = fees.refund;
} FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
else plugin->do_refund (plugin->cls,
{ &ref[i],
ref[i].coin = depos[i].coin; &fees.deposit,
RND_BLK(&ref[i].details.merchant_pub); known_coin_id,
RND_BLK(&ref[i].details.merchant_sig); &not_found,
RND_BLK(&ref[i].details.h_contract_terms); &refund_ok,
RND_BLK(&ref[i].coin.coin_pub); &gone,
ref[i].details.rtransaction_id = i; &conflict));
ref[i].details.refund_amount = value;
ref[i].details.refund_fee = fees.refund;
}
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->do_refund (plugin->cls,
&ref[i],
&fees.deposit,
known_coin_id,
&not_found,
&refund_ok,
&gone,
&conflict));
count++;
break;
case 0://no refund
ref[i].coin=depos[i].coin;
RND_BLK(&ref[i].details.merchant_pub);
RND_BLK(&ref[i].details.merchant_sig);
RND_BLK(&ref[i].details.h_contract_terms);
RND_BLK(&ref[i].coin.coin_pub);
ref[i].details.rtransaction_id = i;
ref[i].details.refund_amount = value;
ref[i].details.refund_fee = fees.refund;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->do_refund (plugin->cls,
&ref[i],
&fees.deposit,
known_coin_id,
&not_found,
&refund_ok,
&gone,
&conflict));
break; break;
}/* END OF SWITCH CASE */ }/* END OF SWITCH CASE */
} }
@ -480,7 +481,6 @@ run (void *cls)
} }
/* End of benchmark setup */ /* End of benchmark setup */
GNUNET_free (perm); GNUNET_free (perm);
// commit
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->commit (plugin->cls)); plugin->commit (plugin->cls));
for (unsigned int r = 0; r < ROUNDS; r++) for (unsigned int r = 0; r < ROUNDS; r++)
@ -500,7 +500,8 @@ run (void *cls)
times = GNUNET_TIME_relative_add (times, times = GNUNET_TIME_relative_add (times,
duration); duration);
duration_sq = duration.rel_value_us * duration.rel_value_us; duration_sq = duration.rel_value_us * duration.rel_value_us;
GNUNET_assert (duration_sq / duration.rel_value_us == duration.rel_value_us); GNUNET_assert (duration_sq / duration.rel_value_us ==
duration.rel_value_us);
GNUNET_assert (sqrs + duration_sq >= sqrs); GNUNET_assert (sqrs + duration_sq >= sqrs);
sqrs += duration_sq; sqrs += duration_sq;
} }
@ -514,10 +515,10 @@ run (void *cls)
ROUNDS); ROUNDS);
avg_dbl = avg.rel_value_us; avg_dbl = avg.rel_value_us;
variance = sqrs - (avg_dbl * avg_dbl * ROUNDS); variance = sqrs - (avg_dbl * avg_dbl * ROUNDS);
fprintf(stdout, fprintf (stdout,
"%8llu ± %6.0f\n", "%8llu ± %6.0f\n",
(unsigned long long) avg.rel_value_us, (unsigned long long) avg.rel_value_us,
sqrt (variance / (ROUNDS-1))); sqrt (variance / (ROUNDS - 1)));
} }
result = 0; result = 0;
drop: drop:
@ -540,12 +541,12 @@ cleanup:
cnt++) cnt++)
destroy_denom_key_pair (new_dkp[cnt]); destroy_denom_key_pair (new_dkp[cnt]);
GNUNET_free (new_dkp); GNUNET_free (new_dkp);
for (unsigned int i=0; i< ROUNDS +1 ; i++) for (unsigned int i = 0; i< ROUNDS + 1; i++)
{ {
TALER_denom_sig_free (&depos[i].coin.denom_sig); TALER_denom_sig_free (&depos[i].coin.denom_sig);
} }
GNUNET_free(depos); GNUNET_free (depos);
GNUNET_free(ref); GNUNET_free (ref);
TALER_EXCHANGEDB_plugin_unload (plugin); TALER_EXCHANGEDB_plugin_unload (plugin);
plugin = NULL; plugin = NULL;
} }
@ -557,7 +558,6 @@ main (int argc,
{ {
const char *plugin_name; const char *plugin_name;
char *config_filename; char *config_filename;
char *testname;
struct GNUNET_CONFIGURATION_Handle *cfg; struct GNUNET_CONFIGURATION_Handle *cfg;
(void) argc; (void) argc;
@ -571,15 +571,17 @@ main (int argc,
"WARNING", "WARNING",
NULL); NULL);
plugin_name++; plugin_name++;
(void) GNUNET_asprintf (&testname, {
"test-exchange-db-%s", char *testname;
plugin_name);
(void) GNUNET_asprintf (&config_filename, GNUNET_asprintf (&testname,
"%s.conf", "test-exchange-db-%s",
testname); plugin_name);
fprintf (stdout, GNUNET_asprintf (&config_filename,
"Using config: %s\n", "%s.conf",
config_filename); testname);
GNUNET_free (testname);
}
cfg = GNUNET_CONFIGURATION_create (); cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_parse (cfg, GNUNET_CONFIGURATION_parse (cfg,
@ -587,16 +589,14 @@ main (int argc,
{ {
GNUNET_break (0); GNUNET_break (0);
GNUNET_free (config_filename); GNUNET_free (config_filename);
GNUNET_free (testname);
return 2; return 2;
} }
GNUNET_SCHEDULER_run (&run, GNUNET_SCHEDULER_run (&run,
cfg); cfg);
GNUNET_CONFIGURATION_destroy (cfg); GNUNET_CONFIGURATION_destroy (cfg);
GNUNET_free (config_filename); GNUNET_free (config_filename);
GNUNET_free (testname);
return result; return result;
} }
/* end of test_exchangedb_by_j.c */ /* end of perf_select_refunds_by_coin.c */

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2022 Taler Systems SA Copyright (C) 2022, 2023 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 General Public License as published by the Free Software terms of the GNU General Public License as published by the Free Software
@ -72,26 +72,16 @@ TEH_PG_aggregate (
now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (), now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (),
pg->aggregator_shift); pg->aggregator_shift);
/* Used in #postgres_aggregate() */ /* Used in #postgres_aggregate() */
PREPARE (pg, PREPARE (pg,
"aggregate", "aggregate",
"WITH rdy AS (" /* find deposits ready by merchant */ "WITH dep AS (" /* restrict to our merchant and account and mark as done */
" SELECT"
" coin_pub"
" FROM deposits_for_matching"
" WHERE refund_deadline<$1" /* filter by shard, only actually executable deposits */
" AND merchant_pub=$2" /* filter by target merchant */
" ORDER BY refund_deadline ASC" /* ordering is not critical */
" LIMIT "
TALER_QUOTE (TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT) /* limits transaction size */
" )"
" ,dep AS (" /* restrict to our merchant and account and mark as done */
" UPDATE deposits" " UPDATE deposits"
" SET done=TRUE" " SET done=TRUE"
" WHERE coin_pub IN (SELECT coin_pub FROM rdy)" " WHERE NOT (done OR policy_blocked)" /* only actually executable deposits */
" AND merchant_pub=$2" /* theoretically, same coin could be spent at another merchant */ " AND refund_deadline<$1" /* filter by shard */
" AND merchant_pub=$2" /* filter by target merchant */
" AND wire_target_h_payto=$3" /* merchant could have a 2nd bank account */ " AND wire_target_h_payto=$3" /* merchant could have a 2nd bank account */
" AND done=FALSE" /* theoretically, same coin could be spend at the same merchant a 2nd time */
" RETURNING" " RETURNING"
" deposit_serial_id" " deposit_serial_id"
" ,coin_pub" " ,coin_pub"

View File

@ -1,914 +0,0 @@
/*
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 exchangedb/pg_batch2_reserves_in_insert.c
* @brief Implementation of the reserves_in_insert function for Postgres
* @author Joseph Xu
*/
#include "platform.h"
#include "taler_error_codes.h"
#include "taler_dbevents.h"
#include "taler_pq_lib.h"
#include "pg_batch2_reserves_in_insert.h"
#include "pg_helper.h"
#include "pg_start.h"
#include "pg_rollback.h"
#include "pg_start_read_committed.h"
#include "pg_commit.h"
#include "pg_reserves_get.h"
#include "pg_reserves_update.h"
#include "pg_setup_wire_target.h"
#include "pg_event_notify.h"
#include "pg_preflight.h"
/**
* Generate event notification for the reserve change.
*
* @param reserve_pub reserve to notfiy on
*/
static char *
compute_notify_on_reserve (const struct TALER_ReservePublicKeyP *reserve_pub)
{
struct TALER_ReserveEventP rep = {
.header.size = htons (sizeof (rep)),
.header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
.reserve_pub = *reserve_pub
};
return GNUNET_PG_get_event_notify_channel (&rep.header);
}
static enum GNUNET_DB_QueryStatus
insert1(struct PostgresClosure *pg,
const struct TALER_EXCHANGEDB_ReserveInInfo reserves[1],
struct GNUNET_TIME_Timestamp expiry,
struct GNUNET_TIME_Timestamp gc,
struct TALER_PaytoHashP h_payto,
char *const * notify_s,
struct GNUNET_TIME_Timestamp reserve_expiration,
bool *transaction_duplicate,
bool *conflict,
uint64_t *reserve_uuid,
enum GNUNET_DB_QueryStatus results[1])
{
enum GNUNET_DB_QueryStatus qs2;
PREPARE (pg,
"batch1_reserve_create",
"SELECT "
" out_reserve_found AS conflicted"
",transaction_duplicate"
",ruuid AS reserve_uuid"
" FROM exchange_do_batch_reserves_in_insert"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12);");
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserves[0].reserve_pub),
GNUNET_PQ_query_param_timestamp (&expiry),
GNUNET_PQ_query_param_timestamp (&gc),
GNUNET_PQ_query_param_uint64 (&reserves[0].wire_reference),
TALER_PQ_query_param_amount (reserves[0].balance),
GNUNET_PQ_query_param_string (reserves[0].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[0].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[0].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_string (notify_s[0]),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("conflicted",
&conflict[0]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate",
&transaction_duplicate[0]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
&reserve_uuid[0]),
GNUNET_PQ_result_spec_end
};
TALER_payto_hash (reserves[0].sender_account_details,
&h_payto);
qs2 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"batch1_reserve_create",
params,
rs);
if (qs2 < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to create reserves 1(%d)\n",
qs2);
results[0] = qs2;
return qs2;
}
GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs2);
if ((! conflict[0]) && transaction_duplicate[0])
{
GNUNET_break (0);
TEH_PG_rollback (pg);
results[0] = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_DB_STATUS_HARD_ERROR;
}
results[0] = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
return qs2;
}
static enum GNUNET_DB_QueryStatus
insert2 (struct PostgresClosure *pg,
const struct TALER_EXCHANGEDB_ReserveInInfo reserves[2],
struct GNUNET_TIME_Timestamp expiry,
struct GNUNET_TIME_Timestamp gc,
struct TALER_PaytoHashP h_payto,
char *const*notify_s,
struct GNUNET_TIME_Timestamp reserve_expiration,
bool *transaction_duplicate,
bool *conflict,
uint64_t *reserve_uuid,
enum GNUNET_DB_QueryStatus results[1])
{
enum GNUNET_DB_QueryStatus qs1;
PREPARE (pg,
"batch2_reserve_create",
"SELECT "
"out_reserve_found AS conflicted"
",out_reserve_found2 AS conflicted2"
",transaction_duplicate"
",transaction_duplicate2"
",ruuid AS reserve_uuid"
",ruuid2 AS reserve_uuid2"
" FROM exchange_do_batch2_reserves_insert"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22);");
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserves[0].reserve_pub),
GNUNET_PQ_query_param_timestamp (&expiry),
GNUNET_PQ_query_param_timestamp (&gc),
GNUNET_PQ_query_param_uint64 (&reserves[0].wire_reference),
TALER_PQ_query_param_amount (reserves[0].balance),
GNUNET_PQ_query_param_string (reserves[0].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[0].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[0].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_string (notify_s[0]),
GNUNET_PQ_query_param_string (notify_s[1]),
GNUNET_PQ_query_param_auto_from_type (reserves[1].reserve_pub),
GNUNET_PQ_query_param_uint64 (&reserves[1].wire_reference),
TALER_PQ_query_param_amount (reserves[1].balance),
GNUNET_PQ_query_param_string (reserves[1].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[1].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[1].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("conflicted",
&conflict[0]),
GNUNET_PQ_result_spec_bool ("conflicted2",
&conflict[1]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate",
&transaction_duplicate[0]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate2",
&transaction_duplicate[1]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
&reserve_uuid[0]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid2",
&reserve_uuid[1]),
GNUNET_PQ_result_spec_end
};
TALER_payto_hash (reserves[0].sender_account_details,
&h_payto);
TALER_payto_hash (reserves[1].sender_account_details,
&h_payto);
qs1 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"batch2_reserve_create",
params,
rs);
if (qs1 < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to create reserves 2(%d)\n",
qs1);
results[0]=qs1;
return qs1;
}
GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1);
/* results[i] = (transaction_duplicate)
? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
: GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;*/
if (
((! conflict[0]) && (transaction_duplicate[0]))
||((! conflict[1]) && (transaction_duplicate[1]))
)
{
GNUNET_break (0);
TEH_PG_rollback (pg); //ROLLBACK
results[0] = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_DB_STATUS_HARD_ERROR;
}
results[0] = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
return qs1;
}
static enum GNUNET_DB_QueryStatus
insert4 (struct PostgresClosure *pg,
const struct TALER_EXCHANGEDB_ReserveInInfo reserves[4],
struct GNUNET_TIME_Timestamp expiry,
struct GNUNET_TIME_Timestamp gc,
struct TALER_PaytoHashP h_payto,
char *const*notify_s,
struct GNUNET_TIME_Timestamp reserve_expiration,
bool *transaction_duplicate,
bool *conflict,
uint64_t *reserve_uuid,
enum GNUNET_DB_QueryStatus results[1])
{
enum GNUNET_DB_QueryStatus qs3;
PREPARE (pg,
"batch4_reserve_create",
"SELECT "
"out_reserve_found AS conflicted"
",out_reserve_found2 AS conflicted2"
",out_reserve_found3 AS conflicted3"
",out_reserve_found4 AS conflicted4"
",transaction_duplicate"
",transaction_duplicate2"
",transaction_duplicate3"
",transaction_duplicate4"
",ruuid AS reserve_uuid"
",ruuid2 AS reserve_uuid2"
",ruuid3 AS reserve_uuid3"
",ruuid4 AS reserve_uuid4"
" FROM exchange_do_batch4_reserves_insert"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34,$35,$36,$37,$38,$39, $40, $41,$42);");
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserves[0].reserve_pub),
GNUNET_PQ_query_param_timestamp (&expiry),
GNUNET_PQ_query_param_timestamp (&gc),
GNUNET_PQ_query_param_uint64 (&reserves[0].wire_reference),
TALER_PQ_query_param_amount (reserves[0].balance),
GNUNET_PQ_query_param_string (reserves[0].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[0].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[0].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_string (notify_s[0]),
GNUNET_PQ_query_param_string (notify_s[1]),
GNUNET_PQ_query_param_string (notify_s[2]),
GNUNET_PQ_query_param_string (notify_s[3]),
GNUNET_PQ_query_param_auto_from_type (reserves[1].reserve_pub),
GNUNET_PQ_query_param_uint64 (&reserves[1].wire_reference),
TALER_PQ_query_param_amount (reserves[1].balance),
GNUNET_PQ_query_param_string (reserves[1].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[1].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[1].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_auto_from_type (reserves[2].reserve_pub),
GNUNET_PQ_query_param_uint64 (&reserves[2].wire_reference),
TALER_PQ_query_param_amount (reserves[2].balance),
GNUNET_PQ_query_param_string (reserves[2].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[2].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[2].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_auto_from_type (reserves[3].reserve_pub),
GNUNET_PQ_query_param_uint64 (&reserves[3].wire_reference),
TALER_PQ_query_param_amount (reserves[3].balance),
GNUNET_PQ_query_param_string (reserves[3].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[3].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[3].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("conflicted",
&conflict[0]),
GNUNET_PQ_result_spec_bool ("conflicted2",
&conflict[1]),
GNUNET_PQ_result_spec_bool ("conflicted3",
&conflict[2]),
GNUNET_PQ_result_spec_bool ("conflicted4",
&conflict[3]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate",
&transaction_duplicate[0]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate2",
&transaction_duplicate[1]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate3",
&transaction_duplicate[2]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate4",
&transaction_duplicate[3]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
&reserve_uuid[0]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid2",
&reserve_uuid[1]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid3",
&reserve_uuid[2]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid4",
&reserve_uuid[3]),
GNUNET_PQ_result_spec_end
};
TALER_payto_hash (reserves[0].sender_account_details,
&h_payto);
TALER_payto_hash (reserves[1].sender_account_details,
&h_payto);
TALER_payto_hash (reserves[2].sender_account_details,
&h_payto);
TALER_payto_hash (reserves[3].sender_account_details,
&h_payto);
qs3 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"batch4_reserve_create",
params,
rs);
if (qs3 < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to create reserves4 (%d)\n",
qs3);
results[0] = qs3;
return qs3;
}
GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs3);
if (
((! conflict[0]) && (transaction_duplicate[0]))
||((! conflict[1]) && (transaction_duplicate[1]))
||((! conflict[2]) && (transaction_duplicate[2]))
||((! conflict[3]) && (transaction_duplicate[3]))
)
{
GNUNET_break (0);
TEH_PG_rollback (pg);
results[0] = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_DB_STATUS_HARD_ERROR;
}
results[0] = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
return qs3;
}
static enum GNUNET_DB_QueryStatus
insert8 (struct PostgresClosure *pg,
const struct TALER_EXCHANGEDB_ReserveInInfo reserves[8],
struct GNUNET_TIME_Timestamp expiry,
struct GNUNET_TIME_Timestamp gc,
struct TALER_PaytoHashP h_payto,
char *const*notify_s,
struct GNUNET_TIME_Timestamp reserve_expiration,
bool *transaction_duplicate,
bool *conflict,
uint64_t *reserve_uuid,
enum GNUNET_DB_QueryStatus results[1])
{
enum GNUNET_DB_QueryStatus qs3;
PREPARE (pg,
"batch8_reserve_create",
"SELECT "
"out_reserve_found AS conflicted"
",out_reserve_found2 AS conflicted2"
",out_reserve_found3 AS conflicted3"
",out_reserve_found4 AS conflicted4"
",out_reserve_found5 AS conflicted5"
",out_reserve_found6 AS conflicted6"
",out_reserve_found7 AS conflicted7"
",out_reserve_found8 AS conflicted8"
",transaction_duplicate"
",transaction_duplicate2"
",transaction_duplicate3"
",transaction_duplicate4"
",transaction_duplicate5"
",transaction_duplicate6"
",transaction_duplicate7"
",transaction_duplicate8"
",ruuid AS reserve_uuid"
",ruuid2 AS reserve_uuid2"
",ruuid3 AS reserve_uuid3"
",ruuid4 AS reserve_uuid4"
",ruuid5 AS reserve_uuid5"
",ruuid6 AS reserve_uuid6"
",ruuid7 AS reserve_uuid7"
",ruuid8 AS reserve_uuid8"
" FROM exchange_do_batch8_reserves_insert"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34,$35,$36,$37,$38,$39, $40, $41,$42,$43,$44,$45,$46,$47,$48,$49,$50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$60,$61,$62,$63,$64,$65,$66,$67,$68,$69,$70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$80,$81,$82);");
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserves[0].reserve_pub),
GNUNET_PQ_query_param_timestamp (&expiry),
GNUNET_PQ_query_param_timestamp (&gc),
GNUNET_PQ_query_param_uint64 (&reserves[0].wire_reference),
TALER_PQ_query_param_amount (reserves[0].balance),
GNUNET_PQ_query_param_string (reserves[0].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[0].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[0].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_string (notify_s[0]),
GNUNET_PQ_query_param_string (notify_s[1]),
GNUNET_PQ_query_param_string (notify_s[2]),
GNUNET_PQ_query_param_string (notify_s[3]),
GNUNET_PQ_query_param_string (notify_s[4]),
GNUNET_PQ_query_param_string (notify_s[5]),
GNUNET_PQ_query_param_string (notify_s[6]),
GNUNET_PQ_query_param_string (notify_s[7]),
GNUNET_PQ_query_param_auto_from_type (reserves[1].reserve_pub),
GNUNET_PQ_query_param_uint64 (&reserves[1].wire_reference),
TALER_PQ_query_param_amount (reserves[1].balance),
GNUNET_PQ_query_param_string (reserves[1].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[1].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[1].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_auto_from_type (reserves[2].reserve_pub),
GNUNET_PQ_query_param_uint64 (&reserves[2].wire_reference),
TALER_PQ_query_param_amount (reserves[2].balance),
GNUNET_PQ_query_param_string (reserves[2].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[2].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[2].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_auto_from_type (reserves[3].reserve_pub),
GNUNET_PQ_query_param_uint64 (&reserves[3].wire_reference),
TALER_PQ_query_param_amount (reserves[3].balance),
GNUNET_PQ_query_param_string (reserves[3].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[3].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[3].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_auto_from_type (reserves[4].reserve_pub),
GNUNET_PQ_query_param_uint64 (&reserves[4].wire_reference),
TALER_PQ_query_param_amount (reserves[4].balance),
GNUNET_PQ_query_param_string (reserves[4].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[4].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[4].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_auto_from_type (reserves[5].reserve_pub),
GNUNET_PQ_query_param_uint64 (&reserves[5].wire_reference),
TALER_PQ_query_param_amount (reserves[5].balance),
GNUNET_PQ_query_param_string (reserves[5].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[5].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[5].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_auto_from_type (reserves[6].reserve_pub),
GNUNET_PQ_query_param_uint64 (&reserves[6].wire_reference),
TALER_PQ_query_param_amount (reserves[6].balance),
GNUNET_PQ_query_param_string (reserves[6].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[6].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[6].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_auto_from_type (reserves[7].reserve_pub),
GNUNET_PQ_query_param_uint64 (&reserves[7].wire_reference),
TALER_PQ_query_param_amount (reserves[7].balance),
GNUNET_PQ_query_param_string (reserves[7].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[7].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[7].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("conflicted",
&conflict[0]),
GNUNET_PQ_result_spec_bool ("conflicted2",
&conflict[1]),
GNUNET_PQ_result_spec_bool ("conflicted3",
&conflict[2]),
GNUNET_PQ_result_spec_bool ("conflicted4",
&conflict[3]),
GNUNET_PQ_result_spec_bool ("conflicted5",
&conflict[4]),
GNUNET_PQ_result_spec_bool ("conflicted6",
&conflict[5]),
GNUNET_PQ_result_spec_bool ("conflicted7",
&conflict[6]),
GNUNET_PQ_result_spec_bool ("conflicted8",
&conflict[7]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate",
&transaction_duplicate[0]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate2",
&transaction_duplicate[1]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate3",
&transaction_duplicate[2]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate4",
&transaction_duplicate[3]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate5",
&transaction_duplicate[4]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate6",
&transaction_duplicate[5]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate7",
&transaction_duplicate[6]),
GNUNET_PQ_result_spec_bool ("transaction_duplicate8",
&transaction_duplicate[7]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
&reserve_uuid[0]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid2",
&reserve_uuid[1]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid3",
&reserve_uuid[2]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid4",
&reserve_uuid[3]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid5",
&reserve_uuid[4]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid6",
&reserve_uuid[5]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid7",
&reserve_uuid[6]),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid8",
&reserve_uuid[7]),
GNUNET_PQ_result_spec_end
};
TALER_payto_hash (reserves[0].sender_account_details,
&h_payto);
TALER_payto_hash (reserves[1].sender_account_details,
&h_payto);
TALER_payto_hash (reserves[2].sender_account_details,
&h_payto);
TALER_payto_hash (reserves[3].sender_account_details,
&h_payto);
TALER_payto_hash (reserves[4].sender_account_details,
&h_payto);
TALER_payto_hash (reserves[5].sender_account_details,
&h_payto);
TALER_payto_hash (reserves[6].sender_account_details,
&h_payto);
TALER_payto_hash (reserves[7].sender_account_details,
&h_payto);
qs3 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"batch8_reserve_create",
params,
rs);
if (qs3 < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to create reserves8 (%d)\n",
qs3);
results[0]=qs3;
return qs3;
}
GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs3);
/* results[i] = (transaction_duplicate)
? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
: GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;*/
if (
((! conflict[0]) && (transaction_duplicate[0]))
||((! conflict[1]) && (transaction_duplicate[1]))
||((! conflict[2]) && (transaction_duplicate[2]))
||((! conflict[3]) && (transaction_duplicate[3]))
||((! conflict[4]) && (transaction_duplicate[4]))
||((! conflict[5]) && (transaction_duplicate[5]))
||((! conflict[6]) && (transaction_duplicate[6]))
||((! conflict[7]) && (transaction_duplicate[7]))
)
{
GNUNET_break (0);
TEH_PG_rollback (pg);
results[0]=GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_DB_STATUS_HARD_ERROR;
}
results[0] = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
return qs3;
}
enum GNUNET_DB_QueryStatus
TEH_PG_batch2_reserves_in_insert (void *cls,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
unsigned int reserves_length,
unsigned int batch_size,
enum GNUNET_DB_QueryStatus *results)
{
struct PostgresClosure *pg = cls;
enum GNUNET_DB_QueryStatus qs1;
enum GNUNET_DB_QueryStatus qs2;
enum GNUNET_DB_QueryStatus qs4;
enum GNUNET_DB_QueryStatus qs5;
struct GNUNET_TIME_Timestamp expiry;
struct GNUNET_TIME_Timestamp gc;
struct TALER_PaytoHashP h_payto;
uint64_t reserve_uuid[reserves_length];
bool transaction_duplicate[reserves_length];
bool need_update = false;
bool t_duplicate=false;
struct GNUNET_TIME_Timestamp reserve_expiration
= GNUNET_TIME_relative_to_timestamp (pg->idle_reserve_expiration_time);
bool conflicts[reserves_length];
char *notify_s[reserves_length];
if (GNUNET_OK !=
TEH_PG_preflight (pg))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
expiry = GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_absolute_add (reserves->execution_time.abs_time,
pg->idle_reserve_expiration_time));
gc = GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
pg->legal_reserve_expiration_time));
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Creating reserve %s with expiration in %s\n",
TALER_B2S (&(reserves->reserve_pub)),
GNUNET_STRINGS_relative_time_to_string (
pg->idle_reserve_expiration_time,
GNUNET_NO));
if (GNUNET_OK !=
TEH_PG_start_read_committed(pg,
"READ_COMMITED"))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
/* Optimistically assume this is a new reserve, create balance for the first
time; we do this before adding the actual transaction to "reserves_in",
as for a new reserve it can't be a duplicate 'add' operation, and as
the 'add' operation needs the reserve entry as a foreign key. */
for (unsigned int i=0;i<reserves_length;i++)
{
const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
notify_s[i] = compute_notify_on_reserve (reserve->reserve_pub);
}
unsigned int i=0;
while (i < reserves_length)
{
unsigned int bs = GNUNET_MIN (batch_size,
reserves_length - i);
if (bs >= 8)
{
qs1=insert8(pg,
&reserves[i],
expiry,
gc,
h_payto,
&notify_s[i],
reserve_expiration,
&transaction_duplicate[i],
&conflicts[i],
&reserve_uuid[i],
&results[i]);
if (qs1<0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to create reserve batch_8 (%d)\n",
qs1);
return qs1;
}
need_update |= conflicts[i];
need_update |= conflicts[i+1];
need_update |= conflicts[i+2];
need_update |= conflicts[i+3];
need_update |= conflicts[i+4];
need_update |= conflicts[i+5];
need_update |= conflicts[i+6];
need_update |= conflicts[i+7];
t_duplicate |= transaction_duplicate[i];
t_duplicate |= transaction_duplicate[i+1];
t_duplicate |= transaction_duplicate[i+2];
t_duplicate |= transaction_duplicate[i+3];
t_duplicate |= transaction_duplicate[i+4];
t_duplicate |= transaction_duplicate[i+5];
t_duplicate |= transaction_duplicate[i+6];
t_duplicate |= transaction_duplicate[i+7];
i+=8;
continue;
}
switch (bs)
{
case 7:
case 6 :
case 5:
case 4 :
qs4=insert4(pg,
&reserves[i],
expiry,
gc,
h_payto,
&notify_s[i],
reserve_expiration,
&transaction_duplicate[i],
&conflicts[i],
&reserve_uuid[i],
&results[i]);
if (qs4<0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to create reserve batch_4 (%d)\n",
qs4);
return qs4;
}
need_update |= conflicts[i];
need_update |= conflicts[i+1];
need_update |= conflicts[i+2];
need_update |= conflicts[i+3];
t_duplicate |= transaction_duplicate[i];
t_duplicate |= transaction_duplicate[i+1];
t_duplicate |= transaction_duplicate[i+2];
t_duplicate |= transaction_duplicate[i+3];
// fprintf(stdout, "reserve_uuid : %ld %ld %ld %ld\n", reserve_uuid[i], reserve_uuid[i+1], reserve_uuid[i+2], reserve_uuid[i+3]);
i += 4;
break;
case 3:
case 2:
qs5=insert2(pg,
&reserves[i],
expiry,
gc,
h_payto,
&notify_s[i],
reserve_expiration,
&transaction_duplicate[i],
&conflicts[i],
&reserve_uuid[i],
&results[i]);
if (qs5<0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to create reserve batch_2 (%d)\n",
qs5);
return qs5;
}
need_update |= conflicts[i];
need_update |= conflicts[i+1];
t_duplicate |= transaction_duplicate[i];
t_duplicate |= transaction_duplicate[i+1];
results[i] = (t_duplicate)
? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
: GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
// fprintf(stdout, "reserve_uuid : %ld %ld\n", reserve_uuid[i], reserve_uuid[i+1]);
i += 2;
break;
case 1:
qs2 = insert1(pg,
&reserves[i],
expiry,
gc,
h_payto,
&notify_s[i],
reserve_expiration,
&transaction_duplicate[i],
&conflicts[i],
&reserve_uuid[i],
&results[i]);
if (qs2<0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to create reserve batch_1 (%d)\n)"
,qs2);
return qs2;
}
need_update |= conflicts[i];
t_duplicate |= transaction_duplicate[i];
// fprintf(stdout, "reserve uuid : %ld c :%d t:%d\n", reserve_uuid[i], conflicts[i], transaction_duplicate[i]);
i += 1;
break;
case 0:
GNUNET_assert (0);
break;
}
} /* end while */
// commit
{
enum GNUNET_DB_QueryStatus cs;
cs = TEH_PG_commit (pg);
if (cs < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to commit\n");
return cs;
}
}
if (! need_update)
{
goto exit;
}
// begin serializable
{
if (GNUNET_OK !=
TEH_PG_start (pg,
"reserve-insert-continued"))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
}
enum GNUNET_DB_QueryStatus qs3;
PREPARE (pg,
"reserves_update",
"SELECT"
" out_duplicate AS duplicate "
"FROM exchange_do_batch_reserves_update"
" ($1,$2,$3,$4,$5,$6,$7,$8);");
for (unsigned int i = 0; i<reserves_length; i++)
{
if (! conflicts[i])
continue;
{
bool duplicate;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserves[i].reserve_pub),
GNUNET_PQ_query_param_timestamp (&expiry),
GNUNET_PQ_query_param_uint64 (&reserves[i].wire_reference),
TALER_PQ_query_param_amount (reserves[i].balance),
GNUNET_PQ_query_param_string (reserves[i].exchange_account_name),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (notify_s[i]),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("duplicate",
&duplicate),
GNUNET_PQ_result_spec_end
};
qs3 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"reserves_update",
params,
rs);
if (qs3<0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to update reserves (%d)\n",
qs3);
results[i] = qs3;
return qs3;
}
results[i] = duplicate
? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
: GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
}
{
enum GNUNET_DB_QueryStatus cs;
cs = TEH_PG_commit (pg);
if (cs < 0)
return cs;
}
exit:
for (unsigned int i = 0; i<reserves_length; i++)
GNUNET_free (notify_s[i]);
return reserves_length;
}

View File

@ -1,34 +0,0 @@
/*
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 exchangedb/pg_batch2_reserves_in_insert.h
* @brief implementation of the batch2_reserves_in_insert function for Postgres
* @author Joseph XU
*/
#ifndef PG_BATCH2_RESERVES_IN_INSERT_H
#define PG_BATCH2_RESERVES_IN_INSERT_H
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
enum GNUNET_DB_QueryStatus
TEH_PG_batch2_reserves_in_insert (void *cls,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
unsigned int reserves_length,
unsigned int batch_size,
enum GNUNET_DB_QueryStatus *results);
#endif

View File

@ -1,595 +0,0 @@
/*
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 exchangedb/pg_batch_reserves_in_insert.c
* @brief Implementation of the reserves_in_insert function for Postgres
* @author Joseph Xu
*/
#include "platform.h"
#include "taler_error_codes.h"
#include "taler_dbevents.h"
#include "taler_pq_lib.h"
#include "pg_batch_reserves_in_insert.h"
#include "pg_helper.h"
#include "pg_start.h"
#include "pg_rollback.h"
#include "pg_start_read_committed.h"
#include "pg_commit.h"
#include "pg_reserves_get.h"
#include "pg_reserves_update.h"
#include "pg_setup_wire_target.h"
#include "pg_event_notify.h"
#include "pg_preflight.h"
/**
* Generate event notification for the reserve change.
*
* @param reserve_pub reserve to notfiy on
*/
static char *
compute_notify_on_reserve (const struct TALER_ReservePublicKeyP *reserve_pub)
{
struct TALER_ReserveEventP rep = {
.header.size = htons (sizeof (rep)),
.header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
.reserve_pub = *reserve_pub
};
return GNUNET_PG_get_event_notify_channel (&rep.header);
}
static enum GNUNET_DB_QueryStatus
insert1(struct PostgresClosure *pg,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserve,
struct GNUNET_TIME_Timestamp expiry,
struct GNUNET_TIME_Timestamp gc,
struct TALER_PaytoHashP h_payto,
const char *notify_s,
struct GNUNET_TIME_Timestamp reserve_expiration,
bool *transaction_duplicate,
bool *conflict,
uint64_t *reserve_uuid)
{
enum GNUNET_DB_QueryStatus qs2;
PREPARE (pg,
"reserve_creates",
"SELECT "
"out_reserve_found AS conflicted"
",transaction_duplicate"
",ruuid AS reserve_uuid"
" FROM exchange_do_batch_reserves_in_insert"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12);");
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&reserve->reserve_pub),
GNUNET_PQ_query_param_timestamp (&expiry),
GNUNET_PQ_query_param_timestamp (&gc),
GNUNET_PQ_query_param_uint64 (&reserve->wire_reference),
TALER_PQ_query_param_amount (&reserve->balance),
GNUNET_PQ_query_param_string (reserve->exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserve->execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserve->sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_string (notify_s),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("conflicted",
conflict),
GNUNET_PQ_result_spec_bool ("transaction_duplicate",
transaction_duplicate),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
reserve_uuid),
GNUNET_PQ_result_spec_end
};
TALER_payto_hash (reserve->sender_account_details,
&h_payto);
/* Note: query uses 'on conflict do nothing' */
qs2 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"reserve_creates",
params,
rs);
if (qs2 < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to create reserves (%d)\n",
qs2);
return qs2;
}
GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs2);
if ((*conflict) && (*transaction_duplicate))
{
GNUNET_break (0);
TEH_PG_rollback (pg);
return GNUNET_DB_STATUS_HARD_ERROR;
}
return qs2;
}
static enum GNUNET_DB_QueryStatus
insert2 (struct PostgresClosure *pg,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserve0,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserve1,
struct GNUNET_TIME_Timestamp expiry,
struct GNUNET_TIME_Timestamp gc,
struct TALER_PaytoHashP h_payto,
const char *notify_s,
struct GNUNET_TIME_Timestamp reserve_expiration,
bool *transaction_duplicate,
bool *transaction_duplicate2,
bool *conflict,
bool *conflict2,
uint64_t *reserve_uuid,
uint64_t *reserve_uuid2)
{
PREPARE (pg,
"reserve_create",
"SELECT "
"out_reserve_found AS conflicted"
",out_reserve_found2 AS conflicted2"
",transaction_duplicate"
",transaction_duplicate2"
",ruuid AS reserve_uuid"
",ruuid2 AS reserve_uuid2"
" FROM exchange_do_batch2_reserves_insert"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21);");
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&reserve0->reserve_pub),
GNUNET_PQ_query_param_timestamp (&expiry),
GNUNET_PQ_query_param_timestamp (&gc),
GNUNET_PQ_query_param_uint64 (&reserve0->wire_reference),
TALER_PQ_query_param_amount (&reserve0->balance),
GNUNET_PQ_query_param_string (reserve0->exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserve0->execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserve0->sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_string (notify_s),
GNUNET_PQ_query_param_auto_from_type (&reserve1->reserve_pub),
GNUNET_PQ_query_param_uint64 (&reserve1->wire_reference),
TALER_PQ_query_param_amount (&reserve1->balance),
GNUNET_PQ_query_param_string (reserve1->exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserve1->execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserve1->sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("conflicted",
conflict),
GNUNET_PQ_result_spec_bool ("conflicted2",
conflict2),
GNUNET_PQ_result_spec_bool ("transaction_duplicate",
transaction_duplicate),
GNUNET_PQ_result_spec_bool ("transaction_duplicate2",
transaction_duplicate2),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
reserve_uuid),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid2",
reserve_uuid2),
GNUNET_PQ_result_spec_end
};
TALER_payto_hash (reserve0->sender_account_details,
&h_payto);
TALER_payto_hash (reserve1->sender_account_details,
&h_payto);
qs1 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"reserve_create",
params,
rs);
if (qs1 < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to create reserves (%d)\n",
qs1);
return qs1;
}
/*
GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1);
results[i] = (transaction_duplicate)
? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
: GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;*/
if ((*conflict) && (*transaction_duplicate) ||((*conflict2) && (*transaction_duplicate2)))
{
GNUNET_break (0);
TEH_PG_rollback (pg);
return GNUNET_DB_STATUS_HARD_ERROR;
}
}
static enum GNUNET_DB_QueryStatus
insert4 (struct PostgresClosure *pg,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserve0,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserve1,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserve2,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserve3,
struct GNUNET_TIME_Timestamp expiry,
struct GNUNET_TIME_Timestamp gc,
struct TALER_PaytoHashP h_payto,
const char *notify_s,
struct GNUNET_TIME_Timestamp reserve_expiration,
bool *transaction_duplicate,
bool *transaction_duplicate2,
bool *transaction_duplicate3,
bool *transaction_duplicate4,
bool *conflict,
bool *conflict2,
bool *conflict3,
bool *conflict4,
uint64_t *reserve_uuid,
uint64_t *reserve_uuid2,
uint64_t *reserve_uuid3,
uint64_t *reserve_uuid4)
{
PREPARE (pg,
"reserve_create",
"SELECT "
"out_reserve_found AS conflicted"
",out_reserve_found2 AS conflicted2"
",out_reserve_found3 AS conflicted3"
",out_reserve_found4 AS conflicted4"
",transaction_duplicate"
",transaction_duplicate2"
",transaction_duplicate3"
",transaction_duplicate4"
",ruuid AS reserve_uuid"
",ruuid2 AS reserve_uuid2"
",ruuid3 AS reserve_uuid3"
",ruuid4 AS reserve_uuid4"
" FROM exchange_do_batch4_reserves_insert"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34,$35,$36,$37,$38,$39);");
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&reserve0->reserve_pub),
GNUNET_PQ_query_param_timestamp (&expiry),
GNUNET_PQ_query_param_timestamp (&gc),
GNUNET_PQ_query_param_uint64 (&reserve0->wire_reference),
TALER_PQ_query_param_amount (&reserve0->balance),
GNUNET_PQ_query_param_string (reserve0->exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserve0->execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserve0->sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_string (notify_s),
GNUNET_PQ_query_param_auto_from_type (&reserve1->reserve_pub),
GNUNET_PQ_query_param_uint64 (&reserve1->wire_reference),
TALER_PQ_query_param_amount (&reserve1->balance),
GNUNET_PQ_query_param_string (reserve1->exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserve1->execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserve1->sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_auto_from_type (&reserve2->reserve_pub),
GNUNET_PQ_query_param_timestamp (&expiry),
GNUNET_PQ_query_param_timestamp (&gc),
GNUNET_PQ_query_param_uint64 (&reserve2->wire_reference),
TALER_PQ_query_param_amount (&reserve2->balance),
GNUNET_PQ_query_param_string (reserve2->exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserve2->execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserve2->sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_auto_from_type (&reserve3->reserve_pub),
GNUNET_PQ_query_param_timestamp (&expiry),
GNUNET_PQ_query_param_timestamp (&gc),
GNUNET_PQ_query_param_uint64 (&reserve3->wire_reference),
TALER_PQ_query_param_amount (&reserve3->balance),
GNUNET_PQ_query_param_string (reserve3->exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserve3->execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserve3->sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration)
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("conflicted",
conflict),
GNUNET_PQ_result_spec_bool ("conflicted2",
conflict2),
GNUNET_PQ_result_spec_bool ("conflicted3",
conflict3),
GNUNET_PQ_result_spec_bool ("conflicted4",
conflict4),
GNUNET_PQ_result_spec_bool ("transaction_duplicate",
transaction_duplicate),
GNUNET_PQ_result_spec_bool ("transaction_duplicate2",
transaction_duplicate2),
GNUNET_PQ_result_spec_bool ("transaction_duplicate3",
transaction_duplicate3),
GNUNET_PQ_result_spec_bool ("transaction_duplicate4",
transaction_duplicate4),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
reserve_uuid),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid2",
reserve_uuid2),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid3",
reserve_uuid3),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid4",
reserve_uuid4),
GNUNET_PQ_result_spec_end
};
TALER_payto_hash (reserve0->sender_account_details,
&h_payto);
TALER_payto_hash (reserve1->sender_account_details,
&h_payto);
qs1 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"reserve_create",
params,
rs);
if (qs1 < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to create reserves (%d)\n",
qs1);
return qs1;
}
/*
GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1);
results[i] = (transaction_duplicate)
? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
: GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;*/
if ((*conflict) && (*transaction_duplicate) ||((*conflict2) && (*transaction_duplicate2)))
{
GNUNET_break (0);
TEH_PG_rollback (pg);
return GNUNET_DB_STATUS_HARD_ERROR;
}
}
enum GNUNET_DB_QueryStatus
TEH_PG_batch2_reserves_in_insert (void *cls,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
unsigned int reserves_length,
enum GNUNET_DB_QueryStatus *results)
{
struct PostgresClosure *pg = cls;
enum GNUNET_DB_QueryStatus qs1;
enum GNUNET_DB_QueryStatus qs2;
enum GNUNET_DB_QueryStatus qs4;
enum GNUNET_DB_QueryStatus qs5;
struct GNUNET_TIME_Timestamp expiry;
struct GNUNET_TIME_Timestamp gc;
struct TALER_PaytoHashP h_payto;
uint64_t reserve_uuid[reserves_length];
bool conflicted;
bool conflicted2;
bool transaction_duplicate[reserves_length];
bool need_update = false;
bool need_update2 = false;
struct GNUNET_TIME_Timestamp reserve_expiration
= GNUNET_TIME_relative_to_timestamp (pg->idle_reserve_expiration_time);
bool conflicts[reserves_length];
char *notify_s[reserves_length];
if (GNUNET_OK !=
TEH_PG_preflight (pg))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
expiry = GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_absolute_add (reserves->execution_time.abs_time,
pg->idle_reserve_expiration_time));
gc = GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
pg->legal_reserve_expiration_time));
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Creating reserve %s with expiration in %s\n",
TALER_B2S (&(reserves->reserve_pub)),
GNUNET_STRINGS_relative_time_to_string (
pg->idle_reserve_expiration_time,
GNUNET_NO));
{
if (GNUNET_OK !=
TEH_PG_start_read_committed(pg,
"READ_COMMITED"))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
}
/* Optimistically assume this is a new reserve, create balance for the first
time; we do this before adding the actual transaction to "reserves_in",
as for a new reserve it can't be a duplicate 'add' operation, and as
the 'add' operation needs the reserve entry as a foreign key. */
for (unsigned int i=0;i<reserves_length;i++)
{
const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
notify_s[i] = compute_notify_on_reserve (&reserve->reserve_pub);
}
unsigned int i=0;
while (i < reserves_length)
{
if (reserves_length - i >= 4)
{
qs4=insert4(pg,
&reserves[i],
&reserves[i+2],
&reserves[i+3],
&reserves[i+4],
expiry,
gc,
h_payto,
&notify_s[i],
reserve_expiration,
&transaction_duplicate[i],
&transaction_duplicate[i+1],
&transaction_duplicate[i+2],
&transaction_duplicate[i+3],
&conflicts[i],
&conflicts[i+1],
&conflicts[i+2],
&conflicts[i+3],
&reserve_uuid[i],
&reserve_uuid[i+1],
&reserve_uuid[i+2],
&reserve_uuid[i+3]);
need_update |= conflicts[i];
need_update |= conflicts[i+1];
need_update |= conflicts[i+2];
need_update |= conflicts[i+3];
i += 4;
continue;
}
switch (reserves_length - i)
{
case 3:
case 2:
qs5=insert2(pg,
&reserves[i],
&reserves[i+1],
expiry,
gc,
h_payto,
notify_s[i],
reserve_expiration,
&transaction_duplicate[i],
&transaction_duplicate[i+1],
&conflicts[i],
&conflicts[i+1],
&reserve_uuid[i],
&reserve_uuid[i+1]);
need_update |= conflicts[i];
need_update |= conflicts[i+1];
i += 2;
break;
case 1:
qs2 = insert1(pg,
&reserves[i],
expiry,
gc,
h_payto,
notify_s[i],
reserve_expiration,
&transaction_duplicate[i],
&conflicts[i],
&reserve_uuid[i]);
need_update |= conflicts[i];
i += 1;
break;
case 0:
GNUNET_assert (0);
break;
}
} /* end while */
// commit
{
enum GNUNET_DB_QueryStatus cs;
cs = TEH_PG_commit (pg);
if (cs < 0)
return cs;
}
if (!need_update )
goto exit;
// begin serializable
{
if (GNUNET_OK !=
TEH_PG_start(pg,
"reserve-insert-continued"))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
}
enum GNUNET_DB_QueryStatus qs3;
PREPARE (pg,
"reserves_in_add_transaction",
"CALL exchange_do_batch_reserves_update"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9);");
for (unsigned int i=0;i<reserves_length;i++)
{
if (! conflicts[i])
continue;
{
const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&reserve->reserve_pub),
GNUNET_PQ_query_param_timestamp (&expiry),
GNUNET_PQ_query_param_uint64 (&reserve->wire_reference),
TALER_PQ_query_param_amount (&reserve->balance),
GNUNET_PQ_query_param_string (reserve->exchange_account_name),
GNUNET_PQ_query_param_bool (conflicted),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (notify_s[i]),
GNUNET_PQ_query_param_end
};
qs3 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
"reserves_in_add_transaction",
params);
if (qs3<0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to update reserves (%d)\n",
qs3);
return qs3;
}
}
}
{
enum GNUNET_DB_QueryStatus cs;
cs = TEH_PG_commit (pg);
if (cs < 0)
return cs;
}
exit:
for (unsigned int i=0;i<reserves_length;i++)
GNUNET_free (notify_s[i]);
return reserves_length;
}

View File

@ -1,252 +0,0 @@
/*
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 exchangedb/pg_batch_reserves_in_insert.c
* @brief Implementation of the reserves_in_insert function for Postgres
* @author Joseph Xu
*/
#include "platform.h"
#include "taler_error_codes.h"
#include "taler_dbevents.h"
#include "taler_pq_lib.h"
#include "pg_batch_reserves_in_insert.h"
#include "pg_helper.h"
#include "pg_start.h"
#include "pg_rollback.h"
#include "pg_start_read_committed.h"
#include "pg_commit.h"
#include "pg_reserves_get.h"
#include "pg_reserves_update.h"
#include "pg_setup_wire_target.h"
#include "pg_event_notify.h"
#include "pg_preflight.h"
/**
* Generate event notification for the reserve change.
*
* @param reserve_pub reserve to notfiy on
*/
static char *
compute_notify_on_reserve (const struct TALER_ReservePublicKeyP *reserve_pub)
{
struct TALER_ReserveEventP rep = {
.header.size = htons (sizeof (rep)),
.header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
.reserve_pub = *reserve_pub
};
return GNUNET_PG_get_event_notify_channel (&rep.header);
}
enum GNUNET_DB_QueryStatus
TEH_PG_batch_reserves_in_insert (
void *cls,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
unsigned int reserves_length,
enum GNUNET_DB_QueryStatus *results)
{
struct PostgresClosure *pg = cls;
enum GNUNET_DB_QueryStatus qs1;
struct GNUNET_TIME_Timestamp expiry;
struct GNUNET_TIME_Timestamp gc;
struct TALER_PaytoHashP h_payto;
uint64_t reserve_uuid;
struct GNUNET_TIME_Timestamp reserve_expiration
= GNUNET_TIME_relative_to_timestamp (pg->idle_reserve_expiration_time);
bool conflicts[reserves_length];
char *notify_s[reserves_length];
if (GNUNET_OK !=
TEH_PG_preflight (pg))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
PREPARE (pg,
"reserve_create",
"SELECT "
"out_reserve_found AS conflicted"
",transaction_duplicate"
",ruuid AS reserve_uuid"
" FROM exchange_do_batch_reserves_in_insert"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12);");
expiry = GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_absolute_add (reserves->execution_time.abs_time,
pg->idle_reserve_expiration_time));
gc = GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
pg->legal_reserve_expiration_time));
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Creating reserve %s with expiration in %s\n",
TALER_B2S (reserves->reserve_pub),
GNUNET_STRINGS_relative_time_to_string (
pg->idle_reserve_expiration_time,
GNUNET_NO));
if (GNUNET_OK !=
TEH_PG_start_read_committed (pg,
"READ_COMMITED"))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
/* Optimistically assume this is a new reserve, create balance for the first
time; we do this before adding the actual transaction to "reserves_in",
as for a new reserve it can't be a duplicate 'add' operation, and as
the 'add' operation needs the reserve entry as a foreign key. */
for (unsigned int i = 0; i<reserves_length; i++)
{
const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
notify_s[i] = compute_notify_on_reserve (reserve->reserve_pub);
}
bool need_update = false;
for (unsigned int i = 0; i<reserves_length; i++)
{
bool conflicted;
bool transaction_duplicate;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserves[i].reserve_pub),
GNUNET_PQ_query_param_timestamp (&expiry),
GNUNET_PQ_query_param_timestamp (&gc),
GNUNET_PQ_query_param_uint64 (&reserves[i].wire_reference),
TALER_PQ_query_param_amount (reserves[i].balance),
GNUNET_PQ_query_param_string (reserves[i].exchange_account_name),
GNUNET_PQ_query_param_timestamp (&reserves[i].execution_time),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (reserves[i].sender_account_details),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_string (notify_s[i]),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("conflicted",
&conflicted),
GNUNET_PQ_result_spec_bool ("transaction_duplicate",
&transaction_duplicate),
GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
&reserve_uuid),
GNUNET_PQ_result_spec_end
};
TALER_payto_hash (reserves[i].sender_account_details,
&h_payto);
/* Note: query uses 'on conflict do nothing' */
qs1 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"reserve_create",
params,
rs);
if (qs1 < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to create reserves (%d)\n",
qs1);
results[i] = qs1;
return qs1;
}
GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1);
conflicts[i] = conflicted;
// fprintf(stdout, "%d", conflicts[i]);
if (conflicts[i] && transaction_duplicate)
{
GNUNET_break (0);
results[i] = GNUNET_DB_STATUS_HARD_ERROR;
TEH_PG_rollback (pg);
return GNUNET_DB_STATUS_HARD_ERROR;
}
results[i] = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
need_update |= conflicted;
}
// commit
{
enum GNUNET_DB_QueryStatus cs;
cs = TEH_PG_commit (pg);
if (cs < 0)
return cs;
}
if (! need_update)
goto exit;
// begin serializable
{
if (GNUNET_OK !=
TEH_PG_start (pg,
"reserve-insert-continued"))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
}
enum GNUNET_DB_QueryStatus qs2;
PREPARE (pg,
"reserves_in_add_transaction",
"SELECT"
" out_duplicate AS duplicate"
" FROM exchange_do_batch_reserves_update"
" ($1,$2,$3,$4,$5,$6,$7,$8);");
for (unsigned int i = 0; i<reserves_length; i++)
{
if (! conflicts[i])
continue;
{
bool duplicate;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserves[i].reserve_pub),
GNUNET_PQ_query_param_timestamp (&expiry),
GNUNET_PQ_query_param_uint64 (&reserves[i].wire_reference),
TALER_PQ_query_param_amount (reserves[i].balance),
GNUNET_PQ_query_param_string (reserves[i].exchange_account_name),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_string (notify_s[i]),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("duplicate",
&duplicate),
GNUNET_PQ_result_spec_end
};
qs2 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"reserves_in_add_transaction",
params,
rs);
if (qs2 < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to update reserves (%d)\n",
qs2);
results[i] = qs2;
return qs2;
}
results[i] = duplicate
? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
: GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
}
{
enum GNUNET_DB_QueryStatus cs;
cs = TEH_PG_commit (pg);
if (cs < 0)
return cs;
}
exit:
for (unsigned int i = 0; i<reserves_length; i++)
GNUNET_free (notify_s[i]);
return reserves_length;
}

View File

@ -1,35 +0,0 @@
/*
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 exchangedb/pg_batch_reserves_in_insert.h
* @brief implementation of the batch_reserves_in_insert function for Postgres
* @author Christian Grothoff
*/
#ifndef PG_BATCH_RESERVES_IN_INSERT_H
#define PG_BATCH_RESERVES_IN_INSERT_H
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
enum GNUNET_DB_QueryStatus
TEH_PG_batch_reserves_in_insert (void *cls,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
unsigned int reserves_length,
enum GNUNET_DB_QueryStatus *results);
#endif

View File

@ -55,7 +55,7 @@ struct LinkDataContext
/** /**
* Status, set to #GNUNET_SYSERR on errors, * Status, set to #GNUNET_SYSERR on errors,
*/ */
int status; enum GNUNET_GenericReturnValue status;
}; };
@ -190,20 +190,18 @@ TEH_PG_get_link_data (void *cls,
"%d%c", "%d%c",
&percent_refund, &percent_refund,
&dummy)) ) &dummy)) )
{ {
if (NULL != mode) if (NULL != mode)
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Bad mode `%s' specified\n", "Bad mode `%s' specified\n",
mode); mode);
} percent_refund = 0;
if (NULL==mode) }
percent_refund=0;
} }
switch (percent_refund) switch (percent_refund)
{ {
case 0: case 0:
query="get_link"; query = "get_link";
PREPARE (pg, PREPARE (pg,
query, query,
"SELECT " "SELECT "
@ -225,7 +223,7 @@ TEH_PG_get_link_data (void *cls,
" ORDER BY tp.transfer_pub, rrc.freshcoin_index ASC"); " ORDER BY tp.transfer_pub, rrc.freshcoin_index ASC");
break; break;
case 1: case 1:
query="get_link_v1"; query = "get_link_v1";
PREPARE (pg, PREPARE (pg,
query, query,
"WITH rc AS MATERIALIZED (" "WITH rc AS MATERIALIZED ("
@ -252,7 +250,7 @@ TEH_PG_get_link_data (void *cls,
" ORDER BY tp.transfer_pub, rrc.freshcoin_index ASC"); " ORDER BY tp.transfer_pub, rrc.freshcoin_index ASC");
break; break;
case 2: case 2:
query="get_link_v2"; query = "get_link_v2";
PREPARE (pg, PREPARE (pg,
query, query,
"SELECT" "SELECT"
@ -300,5 +298,3 @@ TEH_PG_get_link_data (void *cls,
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
return qs; return qs;
} }

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2022 Taler Systems SA Copyright (C) 2022, 2023 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 General Public License as published by the Free Software terms of the GNU General Public License as published by the Free Software
@ -33,15 +33,16 @@ TEH_PG_get_ready_deposit (void *cls,
struct TALER_MerchantPublicKeyP *merchant_pub, struct TALER_MerchantPublicKeyP *merchant_pub,
char **payto_uri) char **payto_uri)
{ {
static int choose_mode = -2;
struct PostgresClosure *pg = cls; struct PostgresClosure *pg = cls;
struct GNUNET_TIME_Absolute now = {0}; struct GNUNET_TIME_Absolute now
= GNUNET_TIME_absolute_get ();
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_absolute_time (&now), GNUNET_PQ_query_param_absolute_time (&now),
GNUNET_PQ_query_param_uint64 (&start_shard_row), GNUNET_PQ_query_param_uint64 (&start_shard_row),
GNUNET_PQ_query_param_uint64 (&end_shard_row), GNUNET_PQ_query_param_uint64 (&end_shard_row),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("merchant_pub", GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
merchant_pub), merchant_pub),
@ -49,181 +50,70 @@ TEH_PG_get_ready_deposit (void *cls,
payto_uri), payto_uri),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (),
pg->aggregator_shift);
GNUNET_assert (start_shard_row < end_shard_row);
GNUNET_assert (end_shard_row <= INT32_MAX);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Finding ready deposits by deadline %s (%llu)\n",
GNUNET_TIME_absolute2s (now),
(unsigned long long) now.abs_value_us);
int choose_mode =-2;
const char *query; const char *query;
if (-2 == choose_mode) if (-2 == choose_mode)
{ {
const char *mode = getenv ("NEW_LOGIC"); const char *mode = getenv ("TALER_POSTGRES_GET_READY_LOGIC");
char dummy; char dummy;
if ( (NULL==mode) || if ( (NULL==mode) ||
(1 != sscanf (mode, (1 != sscanf (mode,
"%d%c", "%d%c",
&choose_mode, &choose_mode,
&dummy)) ) &dummy)) )
{ {
if (NULL != mode) if (NULL != mode)
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Bad mode `%s' specified\n", "Bad mode `%s' specified\n",
mode); mode);
} choose_mode = 0;
if (NULL==mode) }
choose_mode=0;
} }
switch (choose_mode) switch (choose_mode)
{ {
case 0: case 0:
query="deposits_get_ready"; query = "deposits_get_ready-v5";
PREPARE (pg, PREPARE (pg,
query, query,
"SELECT" "SELECT"
" payto_uri" " payto_uri"
",merchant_pub" ",merchant_pub"
" FROM deposits_by_ready dbr" " FROM deposits dep"
" JOIN deposits dep" " JOIN wire_targets wt"
" ON (dbr.coin_pub = dep.coin_pub AND" " USING (wire_target_h_payto)"
" dbr.deposit_serial_id = dep.deposit_serial_id)" " WHERE NOT (done OR policy_blocked)"
" JOIN wire_targets wt" " AND dep.wire_deadline<=$1"
" USING (wire_target_h_payto)" " AND dep.shard >= $2"
" WHERE dbr.wire_deadline<=$1" " AND dep.shard <= $3"
" AND dbr.shard >= $2"
" AND dbr.shard <= $3"
" ORDER BY " " ORDER BY "
" dbr.wire_deadline ASC" " dep.wire_deadline ASC"
" ,dbr.shard ASC" " ,dep.shard ASC"
" LIMIT 1;"); " LIMIT 1;");
break; break;
case 1: case 1:
query="deposits_get_ready_v1"; query = "deposits_get_ready-v6";
PREPARE (pg, PREPARE (pg,
query, query,
"WITH rc AS MATERIALIZED (" "WITH rc AS MATERIALIZED ("
" SELECT" " SELECT"
" coin_pub" " merchant_pub"
",deposit_serial_id"
" FROM deposits_by_ready"
" WHERE"
" wire_deadline<=$1"
" AND shard >= $2"
" AND shard <= $3"
" ORDER BY "
" wire_deadline ASC"
" ,shard ASC"
" LIMIT 1"
")"
"SELECT"
" wt.payto_uri"
",dep.merchant_pub"
" FROM ("
" SELECT"
" wire_target_h_payto"
",merchant_pub"
" FROM deposits"
" WHERE coin_pub=(SELECT coin_pub FROM rc)"
" AND deposit_serial_id=(SELECT deposit_serial_id FROM rc)"
") dep"
" JOIN wire_targets wt"
" ON (dep.wire_target_h_payto = wt.wire_target_h_payto)"
);
break;
case 2:
query = "stored_procedure_get_ready_deposit";
PREPARE (pg,
query,
"SELECT"
" out_payto_uri AS payto_uri"
",out_merchant_pub AS merchant_pub"
" FROM"
" exchange_do_get_ready_deposit"
" ($1, $2, $3) ");
break;
case 3:
query="deposits_get_ready_v3";
PREPARE (pg,
query,
"WITH rc AS MATERIALIZED ("
" SELECT"
" coin_pub"
",deposit_serial_id"
" FROM deposits_by_ready"
" WHERE"
" wire_deadline<=$1"
" AND shard >= $2"
" AND shard <= $3"
" ORDER BY "
" wire_deadline ASC"
" ,shard ASC"
" LIMIT 1"
")"
"SELECT"
" wt.payto_uri"
",dep.merchant_pub"
" FROM ("
" SELECT"
" wire_target_h_payto"
",merchant_pub"
",coin_pub"
" FROM deposits"
" WHERE coin_pub=(SELECT coin_pub FROM rc)"
" AND deposit_serial_id=(SELECT deposit_serial_id FROM rc)"
") dep"
" JOIN wire_targets wt"
" ON (dep.wire_target_h_payto = wt.wire_target_h_payto)"
" JOIN rc"
" ON (dep.coin_pub=rc.coin_pub)"
);
break;
case 4:
query="deposits_get_ready_v4";
PREPARE (pg,
query,
"WITH rc AS MATERIALIZED ("
" SELECT"
" coin_pub"
",deposit_serial_id"
" FROM deposits_by_ready"
" WHERE"
" wire_deadline<=$1"
" AND shard >= $2"
" AND shard <= $3"
" ORDER BY "
" wire_deadline ASC"
" ,shard ASC"
" LIMIT 1"
"),"
"WITH rv AS MATERIALIZED ("
" SELECT"
" payto_uri"
",wire_target_h_payto" ",wire_target_h_payto"
" FROM wire_targets" " FROM deposits"
" WHERE NOT (done OR policy_blocked)"
" AND wire_deadline<=$1"
" AND shard >= $2"
" AND shard <= $3"
" ORDER BY wire_deadline ASC"
" ,shard ASC"
" LIMIT 1"
")" ")"
"SELECT" "SELECT"
" rv.payto_uri" " wt.payto_uri"
",dep.merchant_pub" ",rc.merchant_pub"
" FROM (" " FROM wire_targets wt"
" SELECT" " JOIN rc"
" wire_target_h_payto" " USING (wire_target_h_payto);");
",merchant_pub"
" FROM deposits"
" WHERE coin_pub=(SELECT coin_pub FROM rc)"
" AND deposit_serial_id=(SELECT deposit_serial_id FROM rc)"
") dep"
" JOIN rv"
" ON (rv.wire_target_h_payto=dep.wire_target_h_payto)"
);
break; break;
default: default:
GNUNET_break (0); GNUNET_break (0);

File diff suppressed because it is too large Load Diff

View File

@ -24,28 +24,26 @@
#include "taler_util.h" #include "taler_util.h"
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h" #include "taler_exchangedb_plugin.h"
/** /**
* Insert an incoming transaction into reserves. New reserves are also * Insert an incoming transaction into reserves. New reserves are also
* created through this function. Started within the scope of an ongoing * created through this function. Runs its own transaction(s).
* transaction.
* *
* @param cls the `struct PostgresClosure` with the plugin-specific state * @param cls the `struct PostgresClosure` with the plugin-specific state
* @param reserve_pub public key of the reserve * @param reserves array of reserves to insert
* @param balance the amount that has to be added to the reserve * @param reserves_length length of the @a reserves array
* @param execution_time when was the amount added * @param batch_size how many inserts to do in one go
* @param sender_account_details account information for the sender (payto://-URL) * @param[out] results set to query status per reserve, must be of length @a reserves_length
* @param exchange_account_section name of the section in the configuration for the exchange's
* account into which the deposit was made
* @param wire_ref unique reference identifying the wire transfer
* @return transaction status code * @return transaction status code
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
TEH_PG_reserves_in_insert (void *cls, TEH_PG_reserves_in_insert (
const struct TALER_ReservePublicKeyP *reserve_pub, void *cls,
const struct TALER_Amount *balance, const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
struct GNUNET_TIME_Timestamp execution_time, unsigned int reserves_length,
const char *sender_account_details, unsigned int batch_size,
const char *exchange_account_section, enum GNUNET_DB_QueryStatus *results);
uint64_t wire_ref);
#endif #endif

View File

@ -129,7 +129,7 @@ TEH_PG_select_refunds_by_coin (
if (-2 == percent_refund) if (-2 == percent_refund)
{ {
const char *mode = getenv ("NEW_LOGIC"); const char *mode = getenv ("TALER_POSTGRES_SELECT_REFUNDS_BY_COIN_LOGIC");
char dummy; char dummy;
if ( (NULL==mode) || if ( (NULL==mode) ||
@ -137,14 +137,13 @@ TEH_PG_select_refunds_by_coin (
"%d%c", "%d%c",
&percent_refund, &percent_refund,
&dummy)) ) &dummy)) )
{ {
if (NULL != mode) if (NULL != mode)
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Bad mode `%s' specified\n", "Bad mode `%s' specified\n",
mode); mode);
} percent_refund = 0;
if (NULL==mode) }
percent_refund=0;
} }
switch (percent_refund) switch (percent_refund)

View File

@ -203,8 +203,6 @@
#include "pg_reserves_update.h" #include "pg_reserves_update.h"
#include "pg_setup_wire_target.h" #include "pg_setup_wire_target.h"
#include "pg_compute_shard.h" #include "pg_compute_shard.h"
#include "pg_batch_reserves_in_insert.h"
#include "pg_batch2_reserves_in_insert.h"
#include "pg_insert_kyc_attributes.h" #include "pg_insert_kyc_attributes.h"
#include "pg_update_kyc_attributes.h" #include "pg_update_kyc_attributes.h"
#include "pg_select_similar_kyc_attributes.h" #include "pg_select_similar_kyc_attributes.h"
@ -747,10 +745,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_select_purse_by_merge_pub; = &TEH_PG_select_purse_by_merge_pub;
plugin->set_purse_balance plugin->set_purse_balance
= &TEH_PG_set_purse_balance; = &TEH_PG_set_purse_balance;
plugin->batch_reserves_in_insert
= &TEH_PG_batch_reserves_in_insert;
plugin->batch2_reserves_in_insert
= &TEH_PG_batch2_reserves_in_insert;
plugin->insert_kyc_attributes plugin->insert_kyc_attributes
= &TEH_PG_insert_kyc_attributes; = &TEH_PG_insert_kyc_attributes;
plugin->update_kyc_attributes plugin->update_kyc_attributes

View File

@ -1,196 +0,0 @@
/*
This file is part of TALER
Copyright (C) 2014-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 exchangedb/test_exchangedb_by_j.c
* @brief test cases for DB interaction functions
* @author Joseph Xu
*/
#include "platform.h"
#include "taler_exchangedb_lib.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
/**o
* Global result from the testcase.
*/
static int result;
/**
* Report line of error if @a cond is true, and jump to label "drop".
*/
#define FAILIF(cond) \
do { \
if (! (cond)) {break;} \
GNUNET_break (0); \
goto drop; \
} while (0)
/**
* Initializes @a ptr with random data.
*/
#define RND_BLK(ptr) \
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr))
/**
* Initializes @a ptr with zeros.
*/
#define ZR_BLK(ptr) \
memset (ptr, 0, sizeof (*ptr))
/**
* Currency we use. Must match test-exchange-db-*.conf.
*/
#define CURRENCY "EUR"
/**
* Database plugin under test.
*/
static struct TALER_EXCHANGEDB_Plugin *plugin;
/**
* Main function that will be run by the scheduler.
*
* @param cls closure with config
*/
static void
run (void *cls)
{
struct GNUNET_CONFIGURATION_Handle *cfg = cls;
const uint32_t num_partitions = 10;
if (NULL ==
(plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
{
GNUNET_break (0);
result = 77;
return;
}
(void) plugin->drop_tables (plugin->cls);
if (GNUNET_OK !=
plugin->create_tables (plugin->cls,
true,
num_partitions))
{
GNUNET_break (0);
result = 77;
goto cleanup;
}
for (unsigned int i = 0; i< 8; i++)
{
static unsigned int batches[] = {1, 1, 2, 3, 4, 16, 64, 256};
const char *sndr = "payto://x-taler-bank/localhost:8080/1";
struct TALER_Amount value;
unsigned int batch_size = batches[i];
struct GNUNET_TIME_Absolute now;
struct GNUNET_TIME_Timestamp ts;
struct GNUNET_TIME_Relative duration;
struct TALER_ReservePublicKeyP reserve_pubs[batch_size];
struct TALER_EXCHANGEDB_ReserveInInfo reserves[batch_size];
enum GNUNET_DB_QueryStatus results[batch_size];
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":1.000010",
&value));
now = GNUNET_TIME_absolute_get ();
ts = GNUNET_TIME_timestamp_get ();
for (unsigned int k = 0; k<batch_size; k++)
{
RND_BLK (&reserve_pubs[k]);
reserves[k].reserve_pub = &reserve_pubs[k];
reserves[k].balance = &value;
reserves[k].execution_time = ts;
reserves[k].sender_account_details = sndr;
reserves[k].exchange_account_name = "name";
reserves[k].wire_reference = k;
}
FAILIF (batch_size !=
plugin->batch_reserves_in_insert (plugin->cls,
reserves,
batch_size,
results));
duration = GNUNET_TIME_absolute_get_duration (now);
fprintf (stdout,
"for a batchsize equal to %d it took %s\n",
batch_size,
GNUNET_STRINGS_relative_time_to_string (duration,
GNUNET_NO) );
}
result = 0;
drop:
GNUNET_break (GNUNET_OK ==
plugin->drop_tables (plugin->cls));
cleanup:
TALER_EXCHANGEDB_plugin_unload (plugin);
plugin = NULL;
}
int
main (int argc,
char *const argv[])
{
const char *plugin_name;
char *config_filename;
char *testname;
struct GNUNET_CONFIGURATION_Handle *cfg;
(void) argc;
result = -1;
if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
{
GNUNET_break (0);
return -1;
}
GNUNET_log_setup (argv[0],
"WARNING",
NULL);
plugin_name++;
(void) GNUNET_asprintf (&testname,
"test-exchange-db-%s",
plugin_name);
(void) GNUNET_asprintf (&config_filename,
"%s.conf",
testname);
fprintf (stdout,
"Using config: %s\n",
config_filename);
cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK !=
GNUNET_CONFIGURATION_parse (cfg,
config_filename))
{
GNUNET_break (0);
GNUNET_free (config_filename);
GNUNET_free (testname);
return 2;
}
GNUNET_SCHEDULER_run (&run,
cfg);
GNUNET_CONFIGURATION_destroy (cfg);
GNUNET_free (config_filename);
GNUNET_free (testname);
return result;
}
/* end of test_exchangedb_by_j.c */

View File

@ -3479,28 +3479,6 @@ struct TALER_EXCHANGEDB_Plugin
struct TALER_PaytoHashP *h_payto); struct TALER_PaytoHashP *h_payto);
/**
* Insert a incoming transaction into reserves. New reserves are
* also created through this function.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param reserve_pub public key of the reserve
* @param balance the amount that has to be added to the reserve
* @param execution_time when was the amount added
* @param sender_account_details information about the sender's bank account, in payto://-format
* @param wire_reference unique reference identifying the wire transfer
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
(*reserves_in_insert)(void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *balance,
struct GNUNET_TIME_Timestamp execution_time,
const char *sender_account_details,
const char *exchange_account_name,
uint64_t wire_reference);
/** /**
* Insert a batch of incoming transaction into reserves. New reserves are * Insert a batch of incoming transaction into reserves. New reserves are
* also created through this function. * also created through this function.
@ -3512,25 +3490,7 @@ struct TALER_EXCHANGEDB_Plugin
* set to the status of the * set to the status of the
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
(*batch_reserves_in_insert)( (*reserves_in_insert)(
void *cls,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
unsigned int reserves_length,
enum GNUNET_DB_QueryStatus *results);
/**
* Insert a batch of incoming transaction into reserves. New reserves are
* also created through this function.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param reserves
* @param reserves_length length of the @a reserves array
* @param[out] results array of transaction status codes of length @a reserves_length,
* set to the status of the
*/
enum GNUNET_DB_QueryStatus
(*batch2_reserves_in_insert)(
void *cls, void *cls,
const struct TALER_EXCHANGEDB_ReserveInInfo *reserves, const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
unsigned int reserves_length, unsigned int reserves_length,
@ -4102,13 +4062,6 @@ struct TALER_EXCHANGEDB_Plugin
char **payto_uri); char **payto_uri);
/**
* Maximum number of results we return from iterate_matching_deposits().
*
* Limit on the number of transactions we aggregate at once.
*/
#define TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT 10000
/** /**
* Aggregate all matching deposits for @a h_payto and * Aggregate all matching deposits for @a h_payto and
* @a merchant_pub, returning the total amounts. * @a merchant_pub, returning the total amounts.