diff --git a/src/exchangedb/.gitignore b/src/exchangedb/.gitignore
index fcac98bc4..abe505527 100644
--- a/src/exchangedb/.gitignore
+++ b/src/exchangedb/.gitignore
@@ -9,3 +9,4 @@ shard-drop0001.sqltest-exchangedb-by-j-postgres
test-exchangedb-by-j-postgres
perf-exchangedb-reserves-in-insert-postgres
exchange-0002.sql
+procedures.sql
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index 4d9bfcb59..a6eb6747c 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -243,6 +243,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
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_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
libtaler_plugin_exchangedb_postgres_la_LIBADD = \
$(LTLIBINTL)
@@ -280,12 +281,16 @@ check_PROGRAMS = \
test-exchangedb-postgres \
bench-db-postgres\
perf-exchangedb-reserves-in-insert-postgres\
- test-exchangedb-by-j-postgres
+ test-exchangedb-by-j-postgres\
+ test-exchangedb-batch-reserves-in-insert-postgres
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
TESTS = \
test-exchangedb-postgres\
test-exchangedb-by-j-postgres\
- perf-exchangedb-reserves-in-insert-postgres
+ perf-exchangedb-reserves-in-insert-postgres\
+ test-exchangedb-batch-reserves-in-insert-postgres
+
+
test_exchangedb_postgres_SOURCES = \
test_exchangedb.c
test_exchangedb_postgres_LDADD = \
@@ -333,5 +338,29 @@ bench_db_postgres_LDADD = \
-lgnunetutil \
$(XLIB)
+
+test_exchangedb_batch_reserves_in_insert_postgres_SOURCES = \
+ test_exchangedb_batch_reserves_in_insert.c
+test_exchangedb_batch_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.c
+bench_db_postgres_LDADD = \
+ libtalerexchangedb.la \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/pq/libtalerpq.la \
+ -lgnunetpq \
+ -lgnunetutil \
+ $(XLIB)
+
+
EXTRA_test_exchangedb_postgres_DEPENDENCIES = \
libtaler_plugin_exchangedb_postgres.la
diff --git a/src/exchangedb/pg_batch2_reserves_in_insert.c b/src/exchangedb/pg_batch2_reserves_in_insert.c
new file mode 100644
index 000000000..8aca11de0
--- /dev/null
+++ b/src/exchangedb/pg_batch2_reserves_in_insert.c
@@ -0,0 +1,282 @@
+/*
+ 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
+ */
+/**
+ * @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_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;
+ struct GNUNET_TIME_Timestamp expiry;
+ struct GNUNET_TIME_Timestamp gc;
+ struct TALER_PaytoHashP h_payto;
+ uint64_t reserve_uuid;
+ uint64_t reserve_uuid2;
+ bool conflicted;
+ bool conflicted2;
+ bool transaction_duplicate;
+ bool transaction_duplicate2;
+ 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];
+ bool conflicts2[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"
+ ",out_reserve_found2 AS conflicted2"
+ ",transaction_duplicate"
+ ",transaction_duplicate2"
+ ",ruuid AS reserve_uuid"
+ ",ruuid2 AS reserve_uuid2"
+ " FROM batch2_reserves_insert"
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21);");
+ 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;ireserve_pub);
+ }
+
+ for (unsigned int i=0;i<(reserves_length & ~1);i+=2)
+ {
+ const struct TALER_EXCHANGEDB_ReserveInInfo *reserve0 = &reserves[i];
+ const struct TALER_EXCHANGEDB_ReserveInInfo *reserve1 = &reserves[i+1];
+ 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[i]),
+ 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",
+ &conflicted),
+ GNUNET_PQ_result_spec_bool ("conflicted2",
+ &conflicted2),
+ 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);
+
+ /* 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);
+ return qs1;
+ }
+ if (reserves_length & 1)
+ {
+ const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[reserves_length-1];
+ // single insert logic here
+ }
+ 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;
+ conflicts[i] = conflicted;
+ conflicts2[i] = conflicted2;
+ // fprintf(stdout, "%d", conflicts[i]);
+ // fprintf(stdout, "%d", conflicts2[i]);
+ if ((!conflicts[i] && transaction_duplicate) ||(!conflicts2[i] && transaction_duplicate2))
+ {
+ GNUNET_break (0);
+ TEH_PG_rollback (pg);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ need_update |= conflicted |= conflicted2;
+ }
+ // 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 batch_reserves_update"
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9);");
+ for (unsigned int i=0;ireserve_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
+ };
+
+ qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "reserves_in_add_transaction",
+ params);
+ if (qs2<0)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to update reserves (%d)\n",
+ qs2);
+ return qs2;
+ }
+ }
+ }
+ {
+ enum GNUNET_DB_QueryStatus cs;
+
+ cs = TEH_PG_commit (pg);
+ if (cs < 0)
+ return cs;
+ }
+
+ exit:
+ for (unsigned int i=0;i
+ */
+/**
+ * @file exchangedb/pg_batch2_reserves_in_insert.h
+ * @brief implementation of the batch2_reserves_in_insert function for Postgres
+ * @author Christian Grothoff
+ */
+#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,
+ enum GNUNET_DB_QueryStatus *results);
+
+#endif
diff --git a/src/exchangedb/pg_batch4_reserves_in_insert.c b/src/exchangedb/pg_batch4_reserves_in_insert.c
new file mode 100644
index 000000000..604a31e78
--- /dev/null
+++ b/src/exchangedb/pg_batch4_reserves_in_insert.c
@@ -0,0 +1,266 @@
+/*
+ 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
+ */
+/**
+ * @file exchangedb/pg_batch4_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_batch4_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;
+ bool conflicted;
+ bool conflicted2;
+ bool transaction_duplicate;
+ bool transaction_duplicate2;
+ 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];
+ bool conflicts2[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"
+ ",out_reserve_found2 AS conflicted2"
+ ",transaction_duplicate"
+ ",transaction_duplicate2"
+ ",ruuid AS reserve_uuid"
+ " FROM batch2_reserves_insert"
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21);");
+ 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;ireserve_pub);
+ }
+
+ for (unsigned int i=0;ireserve_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[i]),
+ GNUNET_PQ_query_param_auto_from_type (&reserve->reserve_pub),
+ 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_end
+ };
+
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("conflicted",
+ &conflicted),
+ GNUNET_PQ_result_spec_bool ("conflicted2",
+ &conflicted2),
+ 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' */
+ 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;
+ conflicts[i] = conflicted;
+ conflicts2[i] = conflicted2;
+ // fprintf(stdout, "%d", conflicts[i]);
+ if (!conflicts[i] && !conflicts2[i]&& transaction_duplicate)
+ {
+ GNUNET_break (0);
+ TEH_PG_rollback (pg);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ need_update |= conflicted |= conflicted2;
+ }
+ // 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 batch_reserves_update"
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9);");
+ for (unsigned int i=0;ireserve_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
+ };
+
+ qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "reserves_in_add_transaction",
+ params);
+ if (qs2<0)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to update reserves (%d)\n",
+ qs2);
+ return qs2;
+ }
+ }
+ }
+ {
+ enum GNUNET_DB_QueryStatus cs;
+
+ cs = TEH_PG_commit (pg);
+ if (cs < 0)
+ return cs;
+ }
+
+ exit:
+ for (unsigned int i=0;i
+ */
+/**
+ * @file exchangedb/pg_batch4_reserves_in_insert.h
+ * @brief implementation of the batch4_reserves_in_insert function for Postgres
+ * @author XU Joseph
+ */
+#ifndef PG_BATCH4_RESERVES_IN_INSERT_H
+#define PG_BATCH4_RESERVES_IN_INSERT_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_batch4_reserves_in_insert (void *cls,
+ const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
+ unsigned int reserves_length,
+ enum GNUNET_DB_QueryStatus *results);
+
+#endif
diff --git a/src/exchangedb/pg_batch_reserves_in_insert.c b/src/exchangedb/pg_batch_reserves_in_insert.c
index 455f080dd..8a14022e7 100644
--- a/src/exchangedb/pg_batch_reserves_in_insert.c
+++ b/src/exchangedb/pg_batch_reserves_in_insert.c
@@ -16,7 +16,7 @@
/**
* @file exchangedb/pg_batch_reserves_in_insert.c
* @brief Implementation of the reserves_in_insert function for Postgres
- * @author JOSEPHxu
+ * @author Joseph Xu
*/
#include "platform.h"
#include "taler_error_codes.h"
@@ -32,18 +32,16 @@
#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.
+ * Generate event notification for the reserve change.
*
- * @param pg plugin state
* @param reserve_pub reserve to notfiy on
*/
-static void
-notify_on_reserve (struct PostgresClosure *pg,
- const struct TALER_ReservePublicKeyP *reserve_pub)
+static char *
+compute_notify_on_reserve (const struct TALER_ReservePublicKeyP *reserve_pub)
{
struct TALER_ReserveEventP rep = {
.header.size = htons (sizeof (rep)),
@@ -51,12 +49,7 @@ notify_on_reserve (struct PostgresClosure *pg,
.reserve_pub = *reserve_pub
};
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Notifying on reserve!\n");
- TEH_PG_event_notify (pg,
- &rep.header,
- NULL,
- 0);
+ return GNUNET_PG_get_event_notify_channel (&rep.header);
}
@@ -75,17 +68,26 @@ TEH_PG_batch_reserves_in_insert (void *cls,
uint64_t reserve_uuid;
bool conflicted;
bool transaction_duplicate;
+ bool need_update = 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;
+ }
PREPARE (pg,
"reserve_create",
"SELECT "
"out_reserve_found AS conflicted"
",transaction_duplicate"
",ruuid AS reserve_uuid"
- " FROM exchange_do_batch_reserves_in"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11);");
+ " FROM batch_reserves_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));
@@ -98,54 +100,152 @@ TEH_PG_batch_reserves_in_insert (void *cls,
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; ireserve_pub);
+ }
+
+ for (unsigned int i=0;ireserve_pub), /*$1*/
- GNUNET_PQ_query_param_timestamp (&expiry), /*$4*/
- GNUNET_PQ_query_param_timestamp (&gc), /*$5*/
- GNUNET_PQ_query_param_uint64 (&reserve->wire_reference), /*6*/
- TALER_PQ_query_param_amount (&reserve->balance), /*7+8*/
- GNUNET_PQ_query_param_string (reserve->exchange_account_name), /*9*/
- GNUNET_PQ_query_param_timestamp (&reserve->execution_time), /*10*/
- GNUNET_PQ_query_param_auto_from_type (&h_payto), /*11*/
- GNUNET_PQ_query_param_string (reserve->sender_account_details),/*12*/
- GNUNET_PQ_query_param_timestamp (&reserve_expiration),/*13*/
+ 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[i]),
GNUNET_PQ_query_param_end
};
- /* We should get all our results into results[]*/
+
struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
- &reserve_uuid),
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 (reserve->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);
return qs1;
- notify_on_reserve (pg,
- &reserve->reserve_pub);
- GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1);
- results[i] = (transaction_duplicate)
+ }
+ 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 ( (! conflicted) && transaction_duplicate)
- TEH_PG_rollback (pg);
+ conflicts[i] = conflicted;
+ // fprintf(stdout, "%d", conflicts[i]);
+ if (!conflicts[i] && transaction_duplicate)
+ {
+ GNUNET_break (0);
+ TEH_PG_rollback (pg);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ 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 batch_reserves_update"
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9);");
+ for (unsigned int i=0;ireserve_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
+ };
+
+ qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "reserves_in_add_transaction",
+ params);
+ if (qs2<0)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to update reserves (%d)\n",
+ qs2);
+ return qs2;
+ }
+ }
+ }
+ {
+ enum GNUNET_DB_QueryStatus cs;
+
+ cs = TEH_PG_commit (pg);
+ if (cs < 0)
+ return cs;
+ }
+
+ exit:
+ for (unsigned int i=0;iconn,
"get_policy_details",
params,
diff --git a/src/exchangedb/pg_select_refunds_by_coin.c b/src/exchangedb/pg_select_refunds_by_coin.c
index 6510ae4a2..84b63a719 100644
--- a/src/exchangedb/pg_select_refunds_by_coin.c
+++ b/src/exchangedb/pg_select_refunds_by_coin.c
@@ -138,15 +138,19 @@ TEH_PG_select_refunds_by_coin (
// FIXME-Joseph
PREPARE (pg,
"get_refunds_by_coin_and_contract",
+ "WITH rc AS MATERIALIZED("
"SELECT"
- " ref.amount_with_fee_val"
- ",ref.amount_with_fee_frac"
- " FROM refunds ref"
- " JOIN deposits dep"
- " USING (coin_pub,deposit_serial_id)"
- " WHERE ref.coin_pub=$1"
- " AND dep.merchant_pub=$2"
- " AND dep.h_contract_terms=$3;");
+ " * FROM refunds ref"
+ "WHERE ref.coin_pub=$1"
+ "AND dep.merchant_pub=$2"
+ "AND dep.h_contract_terms=$3"
+ ")"
+ "SELECT"
+ " ref.amount_with_fee_val"
+ " ,ref.amount_with_fee_frac"
+ "FROM deposits dep"
+ "JOIN rc"
+ " USING (coin_pub,deposit_serial_id)");
}
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"get_refunds_by_coin_and_contract",
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index d04df4677..c805810b1 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -60,7 +60,7 @@
#include
#include
-/**WHAT I ADD**/
+/**NEW INCLUDES**/
#include "pg_insert_purse_request.h"
#include "pg_iterate_active_signkeys.h"
#include "pg_preflight.h"
@@ -205,6 +205,7 @@
#include "pg_setup_wire_target.h"
#include "pg_compute_shard.h"
#include "pg_batch_reserves_in_insert.h"
+#include "pg_batch2_reserves_in_insert.h"
/**
* Set to 1 to enable Postgres auto_explain module. This will
* slow down things a _lot_, but also provide extensive logging
@@ -373,4484 +374,6 @@ TEH_PG_internal_setup (struct PostgresClosure *pg,
}
-/**
- * Closure for #get_refunds_cb().
- */
-struct SelectRefundContext
-{
- /**
- * Function to call on each result.
- */
- TALER_EXCHANGEDB_RefundCoinCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Set to #GNUNET_SYSERR on error.
- */
- int status;
-};
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct SelectRefundContext *`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-get_refunds_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct SelectRefundContext *srctx = cls;
- struct PostgresClosure *pg = srctx->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
- if (GNUNET_OK !=
- srctx->cb (srctx->cb_cls,
- &amount_with_fee))
- return;
- }
-}
-
-
-/* Get the details of a policy, referenced by its hash code
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param hc The hash code under which the details to a particular policy should be found
- * @param[out] details The found details
- * @return query execution status
- * */
-static enum GNUNET_DB_QueryStatus
-postgres_get_policy_details (
- void *cls,
- const struct GNUNET_HashCode *hc,
- struct TALER_PolicyDetails *details)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (hc),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_timestamp ("deadline",
- &details->deadline),
- TALER_PQ_RESULT_SPEC_AMOUNT ("commitment",
- &details->commitment),
- TALER_PQ_RESULT_SPEC_AMOUNT ("accumulated_total",
- &details->accumulated_total),
- TALER_PQ_RESULT_SPEC_AMOUNT ("policy_fee",
- &details->policy_fee),
- TALER_PQ_RESULT_SPEC_AMOUNT ("transferable_amount",
- &details->transferable_amount),
- GNUNET_PQ_result_spec_auto_from_type ("state",
- &details->fulfillment_state),
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_uint64 ("policy_fulfillment_id",
- &details->policy_fulfillment_id),
- &details->no_policy_fulfillment_id),
- GNUNET_PQ_result_spec_end
- };
-
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "get_policy_details",
- params,
- rs);
-}
-
-
-/**
- * Perform melt operation, checking for sufficient balance
- * of the coin and possibly persisting the melt details.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param rms client-contributed input for CS denominations that must be checked for idempotency, or NULL for non-CS withdrawals
- * @param[in,out] refresh refresh operation details; the noreveal_index
- * is set in case the coin was already melted before
- * @param known_coin_id row of the coin in the known_coins table
- * @param[in,out] zombie_required true if the melt must only succeed if the coin is a zombie, set to false if the requirement was satisfied
- * @param[out] balance_ok set to true if the balance was sufficient
- * @return query execution status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_do_melt (
- void *cls,
- const struct TALER_RefreshMasterSecretP *rms,
- struct TALER_EXCHANGEDB_Refresh *refresh,
- uint64_t known_coin_id,
- bool *zombie_required,
- bool *balance_ok)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- NULL == rms
- ? GNUNET_PQ_query_param_null ()
- : GNUNET_PQ_query_param_auto_from_type (rms),
- TALER_PQ_query_param_amount (&refresh->amount_with_fee),
- GNUNET_PQ_query_param_auto_from_type (&refresh->rc),
- GNUNET_PQ_query_param_auto_from_type (&refresh->coin.coin_pub),
- GNUNET_PQ_query_param_auto_from_type (&refresh->coin_sig),
- GNUNET_PQ_query_param_uint64 (&known_coin_id),
- GNUNET_PQ_query_param_uint32 (&refresh->noreveal_index),
- GNUNET_PQ_query_param_bool (*zombie_required),
- GNUNET_PQ_query_param_end
- };
- bool is_null;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_bool ("balance_ok",
- balance_ok),
- GNUNET_PQ_result_spec_bool ("zombie_required",
- zombie_required),
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_uint32 ("noreveal_index",
- &refresh->noreveal_index),
- &is_null),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "call_melt",
- params,
- rs);
- if (is_null)
- refresh->noreveal_index = UINT32_MAX; /* set to very invalid value */
- return qs;
-}
-
-
-/**
- * Perform refund operation, checking for sufficient deposits
- * of the coin and possibly persisting the refund details.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param refund refund operation details
- * @param deposit_fee deposit fee applicable for the coin, possibly refunded
- * @param known_coin_id row of the coin in the known_coins table
- * @param[out] not_found set if the deposit was not found
- * @param[out] refund_ok set if the refund succeeded (below deposit amount)
- * @param[out] gone if the merchant was already paid
- * @param[out] conflict set if the refund ID was re-used
- * @return query execution status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_do_refund (
- void *cls,
- const struct TALER_EXCHANGEDB_Refund *refund,
- const struct TALER_Amount *deposit_fee,
- uint64_t known_coin_id,
- bool *not_found,
- bool *refund_ok,
- bool *gone,
- bool *conflict)
-{
- struct PostgresClosure *pg = cls;
- uint64_t deposit_shard = TEH_PG_compute_shard (&refund->details.merchant_pub);
- struct TALER_Amount amount_without_fee;
- struct GNUNET_PQ_QueryParam params[] = {
- TALER_PQ_query_param_amount (&refund->details.refund_amount),
- TALER_PQ_query_param_amount (&amount_without_fee),
- TALER_PQ_query_param_amount (deposit_fee),
- GNUNET_PQ_query_param_auto_from_type (&refund->details.h_contract_terms),
- GNUNET_PQ_query_param_uint64 (&refund->details.rtransaction_id),
- GNUNET_PQ_query_param_uint64 (&deposit_shard),
- GNUNET_PQ_query_param_uint64 (&known_coin_id),
- GNUNET_PQ_query_param_auto_from_type (&refund->coin.coin_pub),
- GNUNET_PQ_query_param_auto_from_type (&refund->details.merchant_pub),
- GNUNET_PQ_query_param_auto_from_type (&refund->details.merchant_sig),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_bool ("not_found",
- not_found),
- GNUNET_PQ_result_spec_bool ("refund_ok",
- refund_ok),
- GNUNET_PQ_result_spec_bool ("gone",
- gone),
- GNUNET_PQ_result_spec_bool ("conflict",
- conflict),
- GNUNET_PQ_result_spec_end
- };
-
- if (0 >
- TALER_amount_subtract (&amount_without_fee,
- &refund->details.refund_amount,
- &refund->details.refund_fee))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "call_refund",
- params,
- rs);
-}
-
-
-/**
- * Perform recoup operation, checking for sufficient deposits
- * of the coin and possibly persisting the recoup details.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param reserve_pub public key of the reserve to credit
- * @param reserve_out_serial_id row in the reserves_out table justifying the recoup
- * @param coin_bks coin blinding key secret to persist
- * @param coin_pub public key of the coin being recouped
- * @param known_coin_id row of the @a coin_pub in the known_coins table
- * @param coin_sig signature of the coin requesting the recoup
- * @param[in,out] recoup_timestamp recoup timestamp, set if recoup existed
- * @param[out] recoup_ok set if the recoup succeeded (balance ok)
- * @param[out] internal_failure set on internal failures
- * @return query execution status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_do_recoup (
- void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- uint64_t reserve_out_serial_id,
- const union TALER_DenominationBlindingKeyP *coin_bks,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- uint64_t known_coin_id,
- const struct TALER_CoinSpendSignatureP *coin_sig,
- struct GNUNET_TIME_Timestamp *recoup_timestamp,
- bool *recoup_ok,
- bool *internal_failure)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_TIME_Timestamp reserve_gc
- = GNUNET_TIME_relative_to_timestamp (pg->legal_reserve_expiration_time);
- struct GNUNET_TIME_Timestamp reserve_expiration
- = GNUNET_TIME_relative_to_timestamp (pg->idle_reserve_expiration_time);
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- GNUNET_PQ_query_param_uint64 (&reserve_out_serial_id),
- GNUNET_PQ_query_param_auto_from_type (coin_bks),
- GNUNET_PQ_query_param_auto_from_type (coin_pub),
- GNUNET_PQ_query_param_uint64 (&known_coin_id),
- GNUNET_PQ_query_param_auto_from_type (coin_sig),
- GNUNET_PQ_query_param_timestamp (&reserve_gc),
- GNUNET_PQ_query_param_timestamp (&reserve_expiration),
- GNUNET_PQ_query_param_timestamp (recoup_timestamp),
- GNUNET_PQ_query_param_end
- };
- bool is_null;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
- recoup_timestamp),
- &is_null),
- GNUNET_PQ_result_spec_bool ("recoup_ok",
- recoup_ok),
- GNUNET_PQ_result_spec_bool ("internal_failure",
- internal_failure),
- GNUNET_PQ_result_spec_end
- };
-
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "call_recoup",
- params,
- rs);
-}
-
-
-/**
- * Perform recoup-refresh operation, checking for sufficient deposits of the
- * coin and possibly persisting the recoup-refresh details.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param old_coin_pub public key of the old coin to credit
- * @param rrc_serial row in the refresh_revealed_coins table justifying the recoup-refresh
- * @param coin_bks coin blinding key secret to persist
- * @param coin_pub public key of the coin being recouped
- * @param known_coin_id row of the @a coin_pub in the known_coins table
- * @param coin_sig signature of the coin requesting the recoup
- * @param[in,out] recoup_timestamp recoup timestamp, set if recoup existed
- * @param[out] recoup_ok set if the recoup-refresh succeeded (balance ok)
- * @param[out] internal_failure set on internal failures
- * @return query execution status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_do_recoup_refresh (
- void *cls,
- const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
- uint64_t rrc_serial,
- const union TALER_DenominationBlindingKeyP *coin_bks,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- uint64_t known_coin_id,
- const struct TALER_CoinSpendSignatureP *coin_sig,
- struct GNUNET_TIME_Timestamp *recoup_timestamp,
- bool *recoup_ok,
- bool *internal_failure)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (old_coin_pub),
- GNUNET_PQ_query_param_uint64 (&rrc_serial),
- GNUNET_PQ_query_param_auto_from_type (coin_bks),
- GNUNET_PQ_query_param_auto_from_type (coin_pub),
- GNUNET_PQ_query_param_uint64 (&known_coin_id),
- GNUNET_PQ_query_param_auto_from_type (coin_sig),
- GNUNET_PQ_query_param_timestamp (recoup_timestamp),
- GNUNET_PQ_query_param_end
- };
- bool is_null;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
- recoup_timestamp),
- &is_null),
- GNUNET_PQ_result_spec_bool ("recoup_ok",
- recoup_ok),
- GNUNET_PQ_result_spec_bool ("internal_failure",
- internal_failure),
- GNUNET_PQ_result_spec_end
- };
-
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "call_recoup_refresh",
- params,
- rs);
-}
-
-
-/**
- * Compares two indices into an array of hash codes according to
- * GNUNET_CRYPTO_hash_cmp of the content at those index positions.
- *
- * Used in a call qsort_t in order to generate sorted policy_hash_codes.
- */
-static int
-hash_code_cmp (
- const void *hc1,
- const void *hc2,
- void *arg)
-{
- size_t i1 = *(size_t *) hc1;
- size_t i2 = *(size_t *) hc2;
- const struct TALER_PolicyDetails *d = arg;
-
- return GNUNET_CRYPTO_hash_cmp (&d[i1].hash_code,
- &d[i2].hash_code);
-}
-
-
-/**
- * Add a proof of fulfillment into the policy_fulfillments table
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param fulfillment fullfilment transaction data to be added
- * @return query execution status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_add_policy_fulfillment_proof (
- void *cls,
- struct TALER_PolicyFulfillmentTransactionData *fulfillment)
-{
- enum GNUNET_DB_QueryStatus qs;
- struct PostgresClosure *pg = cls;
- size_t count = fulfillment->details_count;
- struct GNUNET_HashCode hcs[count];
-
- /* Create the sorted policy_hash_codes */
- {
- size_t idx[count];
- for (size_t i = 0; i < count; i++)
- idx[i] = i;
-
- /* Sort the indices according to the hash codes of the corresponding
- * details. */
- qsort_r (idx,
- count,
- sizeof(size_t),
- hash_code_cmp,
- fulfillment->details);
-
- /* Finally, concatenate all hash_codes in sorted order */
- for (size_t i = 0; i < count; i++)
- hcs[i] = fulfillment->details[idx[i]].hash_code;
- }
-
-
- /* Now, add the proof to the policy_fulfillments table, retrieve the
- * record_id */
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_timestamp (&fulfillment->timestamp),
- TALER_PQ_query_param_json (fulfillment->proof),
- GNUNET_PQ_query_param_auto_from_type (&fulfillment->h_proof),
- GNUNET_PQ_query_param_fixed_size (hcs,
- count * sizeof(struct GNUNET_HashCode)),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("fulfillment_id",
- &fulfillment->fulfillment_id),
- GNUNET_PQ_result_spec_end
- };
-
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "insert_proof_into_policy_fulfillments",
- params,
- rs);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- return qs;
- }
-
- /* Now, set the states of each entry corresponding to the hash_codes in
- * policy_details accordingly */
- for (size_t i = 0; i < count; i++)
- {
- struct TALER_PolicyDetails *pos = &fulfillment->details[i];
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&pos->hash_code),
- GNUNET_PQ_query_param_timestamp (&pos->deadline),
- TALER_PQ_query_param_amount (&pos->commitment),
- TALER_PQ_query_param_amount (&pos->accumulated_total),
- TALER_PQ_query_param_amount (&pos->policy_fee),
- TALER_PQ_query_param_amount (&pos->transferable_amount),
- GNUNET_PQ_query_param_auto_from_type (&pos->fulfillment_state),
- GNUNET_PQ_query_param_end
- };
-
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "update_policy_details",
- params);
- if (qs < 0)
- return qs;
- }
- }
-
- return qs;
-}
-
-
-/**
- * Get the balance of the specified reserve.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param reserve_pub public key of the reserve
- * @param[out] balance set to the reserve balance
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_reserve_balance (void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- struct TALER_Amount *balance)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
- balance),
- GNUNET_PQ_result_spec_end
- };
-
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "get_reserve_balance",
- params,
- rs);
-}
-
-
-/**
- * Check if we have the specified deposit already in the database.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param h_contract_terms contract to check for
- * @param h_wire wire hash to check for
- * @param coin_pub public key of the coin to check for
- * @param merchant merchant public key to check for
- * @param refund_deadline expected refund deadline
- * @param[out] deposit_fee set to the deposit fee the exchange charged
- * @param[out] exchange_timestamp set to the time when the exchange received the deposit
- * @return 1 if we know this operation,
- * 0 if this exact deposit is unknown to us,
- * otherwise transaction error status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_have_deposit2 (
- void *cls,
- const struct TALER_PrivateContractHashP *h_contract_terms,
- const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_MerchantPublicKeyP *merchant,
- struct GNUNET_TIME_Timestamp refund_deadline,
- struct TALER_Amount *deposit_fee,
- struct GNUNET_TIME_Timestamp *exchange_timestamp)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (coin_pub),
- GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
- GNUNET_PQ_query_param_auto_from_type (merchant),
- GNUNET_PQ_query_param_end
- };
- struct TALER_EXCHANGEDB_Deposit deposit2;
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- &deposit2.amount_with_fee),
- GNUNET_PQ_result_spec_timestamp ("wallet_timestamp",
- &deposit2.timestamp),
- GNUNET_PQ_result_spec_timestamp ("exchange_timestamp",
- exchange_timestamp),
- GNUNET_PQ_result_spec_timestamp ("refund_deadline",
- &deposit2.refund_deadline),
- GNUNET_PQ_result_spec_timestamp ("wire_deadline",
- &deposit2.wire_deadline),
- TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
- deposit_fee),
- GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
- &deposit2.wire_salt),
- GNUNET_PQ_result_spec_string ("receiver_wire_account",
- &deposit2.receiver_wire_account),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_DB_QueryStatus qs;
- struct TALER_MerchantWireHashP h_wire2;
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Getting deposits for coin %s\n",
- TALER_B2S (coin_pub));
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "get_deposit",
- params,
- rs);
- if (0 >= qs)
- return qs;
- TALER_merchant_wire_signature_hash (deposit2.receiver_wire_account,
- &deposit2.wire_salt,
- &h_wire2);
- GNUNET_free (deposit2.receiver_wire_account);
- /* Now we check that the other information in @a deposit
- also matches, and if not report inconsistencies. */
- if ( (GNUNET_TIME_timestamp_cmp (refund_deadline,
- !=,
- deposit2.refund_deadline)) ||
- (0 != GNUNET_memcmp (h_wire,
- &h_wire2) ) )
- {
- /* Inconsistencies detected! Does not match! */
- return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
- }
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
-}
-
-
-/**
- * Aggregate all matching deposits for @a h_payto and
- * @a merchant_pub, returning the total amounts.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param h_payto destination of the wire transfer
- * @param merchant_pub public key of the merchant
- * @param wtid wire transfer ID to set for the aggregate
- * @param[out] total set to the sum of the total deposits minus applicable deposit fees and refunds
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_aggregate (
- void *cls,
- const struct TALER_PaytoHashP *h_payto,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- struct TALER_Amount *total)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_TIME_Absolute now = {0};
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_absolute_time (&now),
- GNUNET_PQ_query_param_auto_from_type (merchant_pub),
- GNUNET_PQ_query_param_auto_from_type (h_payto),
- GNUNET_PQ_query_param_auto_from_type (wtid),
- GNUNET_PQ_query_param_end
- };
- uint64_t sum_deposit_value;
- uint64_t sum_deposit_frac;
- uint64_t sum_refund_value;
- uint64_t sum_refund_frac;
- uint64_t sum_fee_value;
- uint64_t sum_fee_frac;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("sum_deposit_value",
- &sum_deposit_value),
- GNUNET_PQ_result_spec_uint64 ("sum_deposit_fraction",
- &sum_deposit_frac),
- GNUNET_PQ_result_spec_uint64 ("sum_refund_value",
- &sum_refund_value),
- GNUNET_PQ_result_spec_uint64 ("sum_refund_fraction",
- &sum_refund_frac),
- GNUNET_PQ_result_spec_uint64 ("sum_fee_value",
- &sum_fee_value),
- GNUNET_PQ_result_spec_uint64 ("sum_fee_fraction",
- &sum_fee_frac),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_DB_QueryStatus qs;
- struct TALER_Amount sum_deposit;
- struct TALER_Amount sum_refund;
- struct TALER_Amount sum_fee;
- struct TALER_Amount delta;
-
- now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (),
- pg->aggregator_shift);
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "aggregate",
- params,
- rs);
- if (qs < 0)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (pg->currency,
- total));
- return qs;
- }
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (pg->currency,
- &sum_deposit));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (pg->currency,
- &sum_refund));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (pg->currency,
- &sum_fee));
- sum_deposit.value = sum_deposit_frac / TALER_AMOUNT_FRAC_BASE
- + sum_deposit_value;
- sum_deposit.fraction = sum_deposit_frac % TALER_AMOUNT_FRAC_BASE;
- sum_refund.value = sum_refund_frac / TALER_AMOUNT_FRAC_BASE
- + sum_refund_value;
- sum_refund.fraction = sum_refund_frac % TALER_AMOUNT_FRAC_BASE;
- sum_fee.value = sum_fee_frac / TALER_AMOUNT_FRAC_BASE
- + sum_fee_value;
- sum_fee.fraction = sum_fee_frac % TALER_AMOUNT_FRAC_BASE; \
- GNUNET_assert (0 <=
- TALER_amount_subtract (&delta,
- &sum_deposit,
- &sum_refund));
- GNUNET_assert (0 <=
- TALER_amount_subtract (total,
- &delta,
- &sum_fee));
- return qs;
-}
-
-
-/**
- * Create a new entry in the transient aggregation table.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param h_payto destination of the wire transfer
- * @param exchange_account_section exchange account to use
- * @param merchant_pub public key of the merchant receiving the transfer
- * @param wtid the raw wire transfer identifier to be used
- * @param kyc_requirement_row row in legitimization_requirements that need to be satisfied to continue, or 0 for none
- * @param total amount to be wired in the future
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_create_aggregation_transient (
- void *cls,
- const struct TALER_PaytoHashP *h_payto,
- const char *exchange_account_section,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- uint64_t kyc_requirement_row,
- const struct TALER_Amount *total)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- TALER_PQ_query_param_amount (total),
- GNUNET_PQ_query_param_auto_from_type (merchant_pub),
- GNUNET_PQ_query_param_auto_from_type (h_payto),
- GNUNET_PQ_query_param_uint64 (&kyc_requirement_row),
- GNUNET_PQ_query_param_string (exchange_account_section),
- GNUNET_PQ_query_param_auto_from_type (wtid),
- GNUNET_PQ_query_param_end
- };
-
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "create_aggregation_transient",
- params);
-}
-
-
-/**
- * Find existing entry in the transient aggregation table.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param h_payto destination of the wire transfer
- * @param merchant_pub public key of the merchant receiving the transfer
- * @param exchange_account_section exchange account to use
- * @param[out] wtid set to the raw wire transfer identifier to be used
- * @param[out] total existing amount to be wired in the future
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_aggregation_transient (
- void *cls,
- const struct TALER_PaytoHashP *h_payto,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- const char *exchange_account_section,
- struct TALER_WireTransferIdentifierRawP *wtid,
- struct TALER_Amount *total)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (h_payto),
- GNUNET_PQ_query_param_auto_from_type (merchant_pub),
- GNUNET_PQ_query_param_string (exchange_account_section),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
- total),
- GNUNET_PQ_result_spec_auto_from_type ("wtid_raw",
- wtid),
- GNUNET_PQ_result_spec_end
- };
-
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "select_aggregation_transient",
- params,
- rs);
-}
-
-
-/**
- * Closure for #get_refunds_cb().
- */
-struct FindAggregationTransientContext
-{
- /**
- * Function to call on each result.
- */
- TALER_EXCHANGEDB_TransientAggregationCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Set to #GNUNET_SYSERR on error.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct SelectRefundContext *`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-get_transients_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct FindAggregationTransientContext *srctx = cls;
- struct PostgresClosure *pg = srctx->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
- cont = srctx->cb (srctx->cb_cls,
- payto_uri,
- &wtid,
- &merchant_pub,
- &amount);
- GNUNET_free (payto_uri);
- if (! cont)
- break;
- }
-}
-
-
-/**
- * Find existing entry in the transient aggregation table.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param h_payto destination of the wire transfer
- * @param cb function to call on each matching entry
- * @param cb_cls closure for @a cb
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_find_aggregation_transient (
- void *cls,
- const struct TALER_PaytoHashP *h_payto,
- TALER_EXCHANGEDB_TransientAggregationCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (h_payto),
- GNUNET_PQ_query_param_end
- };
- struct FindAggregationTransientContext srctx = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "find_transient_aggregations",
- params,
- &get_transients_cb,
- &srctx);
- if (GNUNET_SYSERR == srctx.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Update existing entry in the transient aggregation table.
- * @a h_payto is only needed for query performance.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param h_payto destination of the wire transfer
- * @param wtid the raw wire transfer identifier to update
- * @param kyc_requirement_row row in legitimization_requirements that need to be satisfied to continue, or 0 for none
- * @param total new total amount to be wired in the future
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_update_aggregation_transient (
- void *cls,
- const struct TALER_PaytoHashP *h_payto,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- uint64_t kyc_requirement_row,
- const struct TALER_Amount *total)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- TALER_PQ_query_param_amount (total),
- GNUNET_PQ_query_param_auto_from_type (h_payto),
- GNUNET_PQ_query_param_auto_from_type (wtid),
- GNUNET_PQ_query_param_uint64 (&kyc_requirement_row),
- GNUNET_PQ_query_param_end
- };
-
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "update_aggregation_transient",
- params);
-}
-
-
-/**
- * Obtain information about deposits that are ready to be executed. Such
- * deposits must not be marked as "done", the execution time must be
- * in the past, and the KYC status must be 'ok'.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param start_shard_row minimum shard row to select
- * @param end_shard_row maximum shard row to select (inclusive)
- * @param[out] merchant_pub set to the public key of a merchant with a ready deposit
- * @param[out] payto_uri set to the account of the merchant, to be freed by caller
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_ready_deposit (void *cls,
- uint64_t start_shard_row,
- uint64_t end_shard_row,
- struct TALER_MerchantPublicKeyP *merchant_pub,
- char **payto_uri)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_TIME_Absolute now = {0};
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_absolute_time (&now),
- GNUNET_PQ_query_param_uint64 (&start_shard_row),
- GNUNET_PQ_query_param_uint64 (&end_shard_row),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
- merchant_pub),
- GNUNET_PQ_result_spec_string ("payto_uri",
- payto_uri),
- 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);
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "deposits_get_ready",
- params,
- rs);
-}
-
-
-/**
- * Retrieve the record for a known coin.
- *
- * @param cls the plugin closure
- * @param coin_pub the public key of the coin to search for
- * @param coin_info place holder for the returned coin information object
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_known_coin (void *cls,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- struct TALER_CoinPublicInfo *coin_info)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (coin_pub),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
- &coin_info->denom_pub_hash),
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
- &coin_info->h_age_commitment),
- &coin_info->no_age_commitment),
- TALER_PQ_result_spec_denom_sig ("denom_sig",
- &coin_info->denom_sig),
- GNUNET_PQ_result_spec_end
- };
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Getting known coin data for coin %s\n",
- TALER_B2S (coin_pub));
- coin_info->coin_pub = *coin_pub;
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "get_known_coin",
- params,
- rs);
-}
-
-
-/**
- * Retrieve the denomination of a known coin.
- *
- * @param cls the plugin closure
- * @param coin_pub the public key of the coin to search for
- * @param[out] known_coin_id set to the ID of the coin in the known_coins table
- * @param[out] denom_hash where to store the hash of the coins denomination
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_coin_denomination (
- void *cls,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- uint64_t *known_coin_id,
- struct TALER_DenominationHashP *denom_hash)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (coin_pub),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
- denom_hash),
- GNUNET_PQ_result_spec_uint64 ("known_coin_id",
- known_coin_id),
- GNUNET_PQ_result_spec_end
- };
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Getting coin denomination of coin %s\n",
- TALER_B2S (coin_pub));
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "get_coin_denomination",
- params,
- rs);
-}
-
-
-/**
- * Count the number of known coins by denomination.
- *
- * @param cls database connection plugin state
- * @param denom_pub_hash denomination to count by
- * @return number of coins if non-negative, otherwise an `enum GNUNET_DB_QueryStatus`
- */
-static long long
-postgres_count_known_coins (void *cls,
- const struct
- TALER_DenominationHashP *denom_pub_hash)
-{
- struct PostgresClosure *pg = cls;
- uint64_t count;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("count",
- &count),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "count_known_coins",
- params,
- rs);
- if (0 > qs)
- return (long long) qs;
- return (long long) count;
-}
-
-
-/**
- * Make sure the given @a coin is known to the database.
- *
- * @param cls database connection plugin state
- * @param coin the coin that must be made known
- * @param[out] known_coin_id set to the unique row of the coin
- * @param[out] denom_hash set to the denomination hash of the existing
- * coin (for conflict error reporting)
- * @param[out] h_age_commitment set to the conflicting age commitment hash on conflict
- * @return database transaction status, non-negative on success
- */
-static enum TALER_EXCHANGEDB_CoinKnownStatus
-postgres_ensure_coin_known (void *cls,
- const struct TALER_CoinPublicInfo *coin,
- uint64_t *known_coin_id,
- struct TALER_DenominationHashP *denom_hash,
- struct TALER_AgeCommitmentHash *h_age_commitment)
-{
- struct PostgresClosure *pg = cls;
- enum GNUNET_DB_QueryStatus qs;
- bool existed;
- bool is_denom_pub_hash_null = false;
- bool is_age_hash_null = false;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&coin->coin_pub),
- GNUNET_PQ_query_param_auto_from_type (&coin->denom_pub_hash),
- GNUNET_PQ_query_param_auto_from_type (&coin->h_age_commitment),
- TALER_PQ_query_param_denom_sig (&coin->denom_sig),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_bool ("existed",
- &existed),
- GNUNET_PQ_result_spec_uint64 ("known_coin_id",
- known_coin_id),
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
- denom_hash),
- &is_denom_pub_hash_null),
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
- h_age_commitment),
- &is_age_hash_null),
- GNUNET_PQ_result_spec_end
- };
-
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "insert_known_coin",
- params,
- rs);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- GNUNET_break (0);
- return TALER_EXCHANGEDB_CKS_HARD_FAIL;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- return TALER_EXCHANGEDB_CKS_SOFT_FAIL;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- GNUNET_break (0); /* should be impossible */
- return TALER_EXCHANGEDB_CKS_HARD_FAIL;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- if (! existed)
- return TALER_EXCHANGEDB_CKS_ADDED;
- break; /* continued below */
- }
-
- if ( (! is_denom_pub_hash_null) &&
- (0 != GNUNET_memcmp (&denom_hash->hash,
- &coin->denom_pub_hash.hash)) )
- {
- GNUNET_break_op (0);
- return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT;
- }
-
- if ( (! is_age_hash_null) &&
- (0 != GNUNET_memcmp (h_age_commitment,
- &coin->h_age_commitment)) )
- {
- GNUNET_break (GNUNET_is_zero (h_age_commitment));
- GNUNET_break_op (0);
- return TALER_EXCHANGEDB_CKS_AGE_CONFLICT;
- }
-
- return TALER_EXCHANGEDB_CKS_PRESENT;
-}
-
-
-enum GNUNET_DB_QueryStatus
-setup_wire_target (
- struct PostgresClosure *pg,
- const char *payto_uri,
- struct TALER_PaytoHashP *h_payto)
-{
- struct GNUNET_PQ_QueryParam iparams[] = {
- GNUNET_PQ_query_param_auto_from_type (h_payto),
- GNUNET_PQ_query_param_string (payto_uri),
- GNUNET_PQ_query_param_end
- };
-
- TALER_payto_hash (payto_uri,
- h_payto);
-
- PREPARE (pg,
- "insert_kyc_status",
- "INSERT INTO wire_targets"
- " (wire_target_h_payto"
- " ,payto_uri"
- " ) VALUES "
- " ($1, $2)"
- " ON CONFLICT DO NOTHING");
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_kyc_status",
- iparams);
-}
-
-
-/**
- * Insert information about deposited coin into the database.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param exchange_timestamp time the exchange received the deposit request
- * @param deposit deposit information to store
- * @return query result status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_deposit (void *cls,
- struct GNUNET_TIME_Timestamp exchange_timestamp,
- const struct TALER_EXCHANGEDB_Deposit *deposit)
-{
- struct PostgresClosure *pg = cls;
- struct TALER_PaytoHashP h_payto;
- enum GNUNET_DB_QueryStatus qs;
-
- qs = setup_wire_target (pg,
- deposit->receiver_wire_account,
- &h_payto);
- if (qs < 0)
- return qs;
- if (GNUNET_TIME_timestamp_cmp (deposit->wire_deadline,
- <,
- deposit->refund_deadline))
- {
- GNUNET_break (0);
- }
- {
- uint64_t shard = TEH_PG_compute_shard (&deposit->merchant_pub);
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
- TALER_PQ_query_param_amount (&deposit->amount_with_fee),
- GNUNET_PQ_query_param_timestamp (&deposit->timestamp),
- GNUNET_PQ_query_param_timestamp (&deposit->refund_deadline),
- GNUNET_PQ_query_param_timestamp (&deposit->wire_deadline),
- GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
- GNUNET_PQ_query_param_auto_from_type (&deposit->h_contract_terms),
- GNUNET_PQ_query_param_auto_from_type (&deposit->wire_salt),
- GNUNET_PQ_query_param_auto_from_type (&h_payto),
- GNUNET_PQ_query_param_auto_from_type (&deposit->csig),
- GNUNET_PQ_query_param_timestamp (&exchange_timestamp),
- GNUNET_PQ_query_param_uint64 (&shard),
- GNUNET_PQ_query_param_end
- };
-
- GNUNET_assert (shard <= INT32_MAX);
- GNUNET_log (
- GNUNET_ERROR_TYPE_INFO,
- "Inserting deposit to be executed at %s (%llu/%llu)\n",
- GNUNET_TIME_timestamp2s (deposit->wire_deadline),
- (unsigned long long) deposit->wire_deadline.abs_time.abs_value_us,
- (unsigned long long) deposit->refund_deadline.abs_time.abs_value_us);
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_deposit",
- params);
- }
-}
-
-
-/**
- * Insert information about refunded coin into the database.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param refund refund information to store
- * @return query result status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_refund (void *cls,
- const struct TALER_EXCHANGEDB_Refund *refund)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&refund->coin.coin_pub),
- GNUNET_PQ_query_param_auto_from_type (&refund->details.merchant_pub),
- GNUNET_PQ_query_param_auto_from_type (&refund->details.merchant_sig),
- GNUNET_PQ_query_param_auto_from_type (&refund->details.h_contract_terms),
- GNUNET_PQ_query_param_uint64 (&refund->details.rtransaction_id),
- TALER_PQ_query_param_amount (&refund->details.refund_amount),
- GNUNET_PQ_query_param_end
- };
-
- GNUNET_assert (GNUNET_YES ==
- TALER_amount_cmp_currency (&refund->details.refund_amount,
- &refund->details.refund_fee));
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_refund",
- params);
-}
-
-
-/**
- * Select refunds by @a coin_pub, @a merchant_pub and @a h_contract.
- *
- * @param cls closure of plugin
- * @param coin_pub coin to get refunds for
- * @param merchant_pub merchant to get refunds for
- * @param h_contract contract (hash) to get refunds for
- * @param cb function to call for each refund found
- * @param cb_cls closure for @a cb
- * @return query result status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_refunds_by_coin (
- void *cls,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- const struct TALER_PrivateContractHashP *h_contract,
- TALER_EXCHANGEDB_RefundCoinCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (coin_pub),
- GNUNET_PQ_query_param_auto_from_type (merchant_pub),
- GNUNET_PQ_query_param_auto_from_type (h_contract),
- GNUNET_PQ_query_param_end
- };
- struct SelectRefundContext srctx = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "get_refunds_by_coin_and_contract",
- params,
- &get_refunds_cb,
- &srctx);
- if (GNUNET_SYSERR == srctx.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Lookup refresh melt commitment data under the given @a rc.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param rc commitment hash to use to locate the operation
- * @param[out] melt where to store the result; note that
- * melt->session.coin.denom_sig will be set to NULL
- * and is not fetched by this routine (as it is not needed by the client)
- * @param[out] melt_serial_id set to the row ID of @a rc in the refresh_commitments table
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_melt (void *cls,
- const struct TALER_RefreshCommitmentP *rc,
- struct TALER_EXCHANGEDB_Melt *melt,
- uint64_t *melt_serial_id)
-{
- struct PostgresClosure *pg = cls;
- bool h_age_commitment_is_null;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (rc),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
- &melt->session.coin.
- denom_pub_hash),
- TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
- &melt->melt_fee),
- GNUNET_PQ_result_spec_uint32 ("noreveal_index",
- &melt->session.noreveal_index),
- GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
- &melt->session.coin.coin_pub),
- GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
- &melt->session.coin_sig),
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
- &melt->session.coin.h_age_commitment),
- &h_age_commitment_is_null),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- &melt->session.amount_with_fee),
- GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
- melt_serial_id),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_DB_QueryStatus qs;
-
- memset (&melt->session.coin.denom_sig,
- 0,
- sizeof (melt->session.coin.denom_sig));
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "get_melt",
- params,
- rs);
- if (h_age_commitment_is_null)
- memset (&melt->session.coin.h_age_commitment,
- 0,
- sizeof(melt->session.coin.h_age_commitment));
-
- melt->session.rc = *rc;
- return qs;
-}
-
-
-/**
- * Store in the database which coin(s) the wallet wanted to create
- * in a given refresh operation and all of the other information
- * we learned or created in the /refresh/reveal step.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param melt_serial_id row ID of the commitment / melt operation in refresh_commitments
- * @param num_rrcs number of coins to generate, size of the @a rrcs array
- * @param rrcs information about the new coins
- * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1
- * @param tprivs transfer private keys to store
- * @param tp public key to store
- * @return query status for the transaction
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_refresh_reveal (
- void *cls,
- uint64_t melt_serial_id,
- uint32_t num_rrcs,
- const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
- unsigned int num_tprivs,
- const struct TALER_TransferPrivateKeyP *tprivs,
- const struct TALER_TransferPublicKeyP *tp)
-{
- struct PostgresClosure *pg = cls;
-
- if (TALER_CNC_KAPPA != num_tprivs + 1)
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- for (uint32_t i = 0; iorig_coin_link_sig),
- GNUNET_PQ_query_param_auto_from_type (&rrc->h_denom_pub),
- TALER_PQ_query_param_blinded_planchet (&rrc->blinded_planchet),
- TALER_PQ_query_param_exchange_withdraw_values (&rrc->exchange_vals),
- GNUNET_PQ_query_param_auto_from_type (&rrc->coin_envelope_hash),
- TALER_PQ_query_param_blinded_denom_sig (&rrc->coin_sig),
- GNUNET_PQ_query_param_end
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_refresh_revealed_coin",
- params);
- if (0 > qs)
- return qs;
- }
-
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&melt_serial_id),
- GNUNET_PQ_query_param_auto_from_type (tp),
- GNUNET_PQ_query_param_fixed_size (
- tprivs,
- num_tprivs * sizeof (struct TALER_TransferPrivateKeyP)),
- GNUNET_PQ_query_param_end
- };
-
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_refresh_transfer_keys",
- params);
- }
-}
-
-
-/**
- * Context where we aggregate data from the database.
- * Closure for #add_revealed_coins().
- */
-struct GetRevealContext
-{
- /**
- * Array of revealed coins we obtained from the DB.
- */
- struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs;
-
- /**
- * Length of the @a rrcs array.
- */
- unsigned int rrcs_len;
-
- /**
- * Set to an error code if we ran into trouble.
- */
- enum GNUNET_DB_QueryStatus qs;
-};
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct GetRevealContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-add_revealed_coins (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct GetRevealContext *grctx = cls;
-
- if (0 == num_results)
- return;
- grctx->rrcs = GNUNET_new_array (num_results,
- struct TALER_EXCHANGEDB_RefreshRevealedCoin);
- grctx->rrcs_len = num_results;
- for (unsigned int i = 0; i < num_results; i++)
- {
- uint32_t off;
- struct GNUNET_PQ_ResultSpec rso[] = {
- GNUNET_PQ_result_spec_uint32 ("freshcoin_index",
- &off),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rso,
- i))
- {
- GNUNET_break (0);
- grctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return;
- }
- if (off >= num_results)
- {
- GNUNET_break (0);
- grctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return;
- }
- {
- struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &grctx->rrcs[off];
- struct GNUNET_PQ_ResultSpec rsi[] = {
- /* NOTE: freshcoin_index selected and discarded here... */
- GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
- &rrc->h_denom_pub),
- GNUNET_PQ_result_spec_auto_from_type ("link_sig",
- &rrc->orig_coin_link_sig),
- GNUNET_PQ_result_spec_auto_from_type ("h_coin_ev",
- &rrc->coin_envelope_hash),
- TALER_PQ_result_spec_blinded_planchet ("coin_ev",
- &rrc->blinded_planchet),
- TALER_PQ_result_spec_exchange_withdraw_values ("ewv",
- &rrc->exchange_vals),
- TALER_PQ_result_spec_blinded_denom_sig ("ev_sig",
- &rrc->coin_sig),
- GNUNET_PQ_result_spec_end
- };
-
- if (TALER_DENOMINATION_INVALID != rrc->blinded_planchet.cipher)
- {
- /* duplicate offset, not allowed */
- GNUNET_break (0);
- grctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return;
- }
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rsi,
- i))
- {
- GNUNET_break (0);
- grctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return;
- }
- }
- }
-}
-
-
-/**
- * Lookup in the database the coins that we want to
- * create in the given refresh operation.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param rc identify commitment and thus refresh operation
- * @param cb function to call with the results
- * @param cb_cls closure for @a cb
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_refresh_reveal (void *cls,
- const struct TALER_RefreshCommitmentP *rc,
- TALER_EXCHANGEDB_RefreshCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GetRevealContext grctx;
- enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (rc),
- GNUNET_PQ_query_param_end
- };
-
- memset (&grctx,
- 0,
- sizeof (grctx));
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "get_refresh_revealed_coins",
- params,
- &add_revealed_coins,
- &grctx);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- case GNUNET_DB_STATUS_SOFT_ERROR:
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- goto cleanup;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- default: /* can have more than one result */
- break;
- }
- switch (grctx.qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- case GNUNET_DB_STATUS_SOFT_ERROR:
- goto cleanup;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: /* should be impossible */
- break;
- }
-
- /* Pass result back to application */
- cb (cb_cls,
- grctx.rrcs_len,
- grctx.rrcs);
-cleanup:
- for (unsigned int i = 0; i < grctx.rrcs_len; i++)
- {
- struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &grctx.rrcs[i];
-
- TALER_blinded_denom_sig_free (&rrc->coin_sig);
- TALER_blinded_planchet_free (&rrc->blinded_planchet);
- }
- GNUNET_free (grctx.rrcs);
- return qs;
-}
-
-
-/**
- * Closure for #handle_wt_result.
- */
-struct WireTransferResultContext
-{
- /**
- * Function to call on each result.
- */
- TALER_EXCHANGEDB_AggregationDataCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Set to #GNUNET_SYSERR on serious errors.
- */
- int status;
-};
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results. Helper function
- * for #postgres_lookup_wire_transfer().
- *
- * @param cls closure of type `struct WireTransferResultContext *`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-handle_wt_result (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct WireTransferResultContext *ctx = cls;
- struct PostgresClosure *pg = ctx->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
- ctx->cb (ctx->cb_cls,
- rowid,
- &merchant_pub,
- payto_uri,
- &h_payto,
- exec_time,
- &h_contract_terms,
- &denom_pub,
- &coin_pub,
- &amount_with_fee,
- &deposit_fee);
- GNUNET_PQ_cleanup_result (rs);
- }
-}
-
-
-/**
- * Lookup the list of Taler transactions that were aggregated
- * into a wire transfer by the respective @a wtid.
- *
- * @param cls closure
- * @param wtid the raw wire transfer identifier we used
- * @param cb function to call on each transaction found
- * @param cb_cls closure for @a cb
- * @return query status of the transaction
- */
-static enum GNUNET_DB_QueryStatus
-postgres_lookup_wire_transfer (
- void *cls,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- TALER_EXCHANGEDB_AggregationDataCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (wtid),
- GNUNET_PQ_query_param_end
- };
- struct WireTransferResultContext ctx;
- enum GNUNET_DB_QueryStatus qs;
-
- ctx.cb = cb;
- ctx.cb_cls = cb_cls;
- ctx.pg = pg;
- ctx.status = GNUNET_OK;
- /* check if the melt record exists and get it */
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "lookup_transactions",
- params,
- &handle_wt_result,
- &ctx);
- if (GNUNET_OK != ctx.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Try to find the wire transfer details for a deposit operation.
- * If we did not execute the deposit yet, return when it is supposed
- * to be executed.
- *
- * @param cls closure
- * @param h_contract_terms hash of the proposal data
- * @param h_wire hash of merchant wire details
- * @param coin_pub public key of deposited coin
- * @param merchant_pub merchant public key
- * @param[out] pending set to true if the transaction is still pending
- * @param[out] wtid wire transfer identifier, only set if @a pending is false
- * @param[out] exec_time when was the transaction done, or
- * when we expect it to be done (if @a pending is false)
- * @param[out] amount_with_fee set to the total deposited amount
- * @param[out] deposit_fee set to how much the exchange did charge for the deposit
- * @param[out] kyc set to the kyc status of the receiver (if @a pending)
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_lookup_transfer_by_deposit (
- void *cls,
- const struct TALER_PrivateContractHashP *h_contract_terms,
- const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- bool *pending,
- struct TALER_WireTransferIdentifierRawP *wtid,
- struct GNUNET_TIME_Timestamp *exec_time,
- struct TALER_Amount *amount_with_fee,
- struct TALER_Amount *deposit_fee,
- struct TALER_EXCHANGEDB_KycStatus *kyc)
-{
- struct PostgresClosure *pg = cls;
- enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (coin_pub),
- GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
- GNUNET_PQ_query_param_auto_from_type (merchant_pub),
- GNUNET_PQ_query_param_end
- };
- char *payto_uri;
- struct TALER_WireSaltP wire_salt;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("wtid_raw",
- wtid),
- GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
- &wire_salt),
- GNUNET_PQ_result_spec_string ("payto_uri",
- &payto_uri),
- GNUNET_PQ_result_spec_timestamp ("execution_date",
- exec_time),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- amount_with_fee),
- TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
- deposit_fee),
- GNUNET_PQ_result_spec_end
- };
-
- memset (kyc,
- 0,
- sizeof (*kyc));
- /* check if the aggregation record exists and get it */
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "lookup_deposit_wtid",
- params,
- rs);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
- {
- struct TALER_MerchantWireHashP wh;
-
- TALER_merchant_wire_signature_hash (payto_uri,
- &wire_salt,
- &wh);
- GNUNET_PQ_cleanup_result (rs);
- if (0 ==
- GNUNET_memcmp (&wh,
- h_wire))
- {
- *pending = false;
- kyc->ok = true;
- return qs;
- }
- qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
- }
- if (0 > qs)
- return qs;
- *pending = true;
- memset (wtid,
- 0,
- sizeof (*wtid));
- GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "lookup_deposit_wtid returned 0 matching rows\n");
- {
- /* Check if transaction exists in deposits, so that we just
- do not have a WTID yet. In that case, return without wtid
- (by setting 'pending' true). */
- struct GNUNET_PQ_ResultSpec rs2[] = {
- GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
- &wire_salt),
- GNUNET_PQ_result_spec_string ("payto_uri",
- &payto_uri),
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_uint64 ("legitimization_requirement_serial_id",
- &kyc->requirement_row),
- NULL),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- amount_with_fee),
- TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
- deposit_fee),
- GNUNET_PQ_result_spec_timestamp ("wire_deadline",
- exec_time),
- GNUNET_PQ_result_spec_end
- };
-
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "get_deposit_without_wtid",
- params,
- rs2);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
- {
- struct TALER_MerchantWireHashP wh;
-
- if (0 == kyc->requirement_row)
- kyc->ok = true; /* technically: unknown */
- TALER_merchant_wire_signature_hash (payto_uri,
- &wire_salt,
- &wh);
- GNUNET_PQ_cleanup_result (rs);
- if (0 !=
- GNUNET_memcmp (&wh,
- h_wire))
- return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
- }
- return qs;
- }
-}
-
-
-/**
- * Obtain wire fee from database.
- *
- * @param cls closure
- * @param type type of wire transfer the fee applies for
- * @param date for which date do we want the fee?
- * @param[out] start_date when does the fee go into effect
- * @param[out] end_date when does the fee end being valid
- * @param[out] fees how high are the wire fees
- * @param[out] master_sig signature over the above by the exchange master key
- * @return status of the transaction
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_wire_fee (void *cls,
- const char *type,
- struct GNUNET_TIME_Timestamp date,
- struct GNUNET_TIME_Timestamp *start_date,
- struct GNUNET_TIME_Timestamp *end_date,
- struct TALER_WireFeeSet *fees,
- struct TALER_MasterSignatureP *master_sig)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_string (type),
- GNUNET_PQ_query_param_timestamp (&date),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_timestamp ("start_date",
- start_date),
- GNUNET_PQ_result_spec_timestamp ("end_date",
- end_date),
- TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
- &fees->wire),
- TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
- &fees->closing),
- GNUNET_PQ_result_spec_auto_from_type ("master_sig",
- master_sig),
- GNUNET_PQ_result_spec_end
- };
-
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "get_wire_fee",
- params,
- rs);
-}
-
-
-/**
- * Obtain global fees from database.
- *
- * @param cls closure
- * @param date for which date do we want the fee?
- * @param[out] start_date when does the fee go into effect
- * @param[out] end_date when does the fee end being valid
- * @param[out] fees how high are the wire fees
- * @param[out] purse_timeout set to how long we keep unmerged purses
- * @param[out] history_expiration set to how long we keep account histories
- * @param[out] purse_account_limit set to the number of free purses per account
- * @param[out] master_sig signature over the above by the exchange master key
- * @return status of the transaction
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_global_fee (void *cls,
- struct GNUNET_TIME_Timestamp date,
- struct GNUNET_TIME_Timestamp *start_date,
- struct GNUNET_TIME_Timestamp *end_date,
- struct TALER_GlobalFeeSet *fees,
- struct GNUNET_TIME_Relative *purse_timeout,
- struct GNUNET_TIME_Relative *history_expiration,
- uint32_t *purse_account_limit,
- struct TALER_MasterSignatureP *master_sig)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_timestamp (&date),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_timestamp ("start_date",
- start_date),
- GNUNET_PQ_result_spec_timestamp ("end_date",
- end_date),
- TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
- &fees->history),
- TALER_PQ_RESULT_SPEC_AMOUNT ("account_fee",
- &fees->account),
- TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee",
- &fees->purse),
- GNUNET_PQ_result_spec_relative_time ("purse_timeout",
- purse_timeout),
- GNUNET_PQ_result_spec_relative_time ("history_expiration",
- history_expiration),
- GNUNET_PQ_result_spec_uint32 ("purse_account_limit",
- purse_account_limit),
- GNUNET_PQ_result_spec_auto_from_type ("master_sig",
- master_sig),
- GNUNET_PQ_result_spec_end
- };
-
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "get_global_fee",
- params,
- rs);
-}
-
-
-/**
- * Closure for #global_fees_cb().
- */
-struct GlobalFeeContext
-{
- /**
- * Function to call for each global fee block.
- */
- TALER_EXCHANGEDB_GlobalFeeCallback cb;
-
- /**
- * Closure to give to @e rec.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Set to #GNUNET_SYSERR on error.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-global_fees_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct GlobalFeeContext *gctx = cls;
- struct PostgresClosure *pg = gctx->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- break;
- }
- gctx->cb (gctx->cb_cls,
- &fees,
- purse_timeout,
- history_expiration,
- purse_account_limit,
- start_date,
- end_date,
- &master_sig);
- GNUNET_PQ_cleanup_result (rs);
- }
-}
-
-
-/**
- * Obtain global fees from database.
- *
- * @param cls closure
- * @param cb function to call on each fee entry
- * @param cb_cls closure for @a cb
- * @return status of the transaction
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_global_fees (void *cls,
- TALER_EXCHANGEDB_GlobalFeeCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_TIME_Timestamp date
- = GNUNET_TIME_absolute_to_timestamp (
- GNUNET_TIME_absolute_subtract (
- GNUNET_TIME_absolute_get (),
- GNUNET_TIME_UNIT_YEARS));
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_timestamp (&date),
- GNUNET_PQ_query_param_end
- };
- struct GlobalFeeContext gctx = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
-
- return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "get_global_fees",
- params,
- &global_fees_cb,
- &gctx);
-}
-
-
-/**
- * Insert wire transfer fee into database.
- *
- * @param cls closure
- * @param type type of wire transfer this fee applies for
- * @param start_date when does the fee go into effect
- * @param end_date when does the fee end being valid
- * @param fees how high are the wire fees
- * @param master_sig signature over the above by the exchange master key
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_wire_fee (void *cls,
- const char *type,
- struct GNUNET_TIME_Timestamp start_date,
- struct GNUNET_TIME_Timestamp end_date,
- const struct TALER_WireFeeSet *fees,
- const struct TALER_MasterSignatureP *master_sig)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_string (type),
- GNUNET_PQ_query_param_timestamp (&start_date),
- GNUNET_PQ_query_param_timestamp (&end_date),
- TALER_PQ_query_param_amount (&fees->wire),
- TALER_PQ_query_param_amount (&fees->closing),
- GNUNET_PQ_query_param_auto_from_type (master_sig),
- GNUNET_PQ_query_param_end
- };
- struct TALER_WireFeeSet wx;
- struct TALER_MasterSignatureP sig;
- struct GNUNET_TIME_Timestamp sd;
- struct GNUNET_TIME_Timestamp ed;
- enum GNUNET_DB_QueryStatus qs;
-
- qs = postgres_get_wire_fee (pg,
- type,
- start_date,
- &sd,
- &ed,
- &wx,
- &sig);
- if (qs < 0)
- return qs;
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
- {
- if (0 != GNUNET_memcmp (&sig,
- master_sig))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- if (0 !=
- TALER_wire_fee_set_cmp (fees,
- &wx))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- if ( (GNUNET_TIME_timestamp_cmp (sd,
- !=,
- start_date)) ||
- (GNUNET_TIME_timestamp_cmp (ed,
- !=,
- end_date)) )
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- /* equal record already exists */
- return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
- }
-
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_wire_fee",
- params);
-}
-
-
-/**
- * Insert global fee data into database.
- *
- * @param cls closure
- * @param start_date when does the fees go into effect
- * @param end_date when does the fees end being valid
- * @param fees how high is are the global fees
- * @param purse_timeout when do purses time out
- * @param history_expiration how long are account histories preserved
- * @param purse_account_limit how many purses are free per account
- * @param master_sig signature over the above by the exchange master key
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_global_fee (void *cls,
- struct GNUNET_TIME_Timestamp start_date,
- struct GNUNET_TIME_Timestamp end_date,
- const struct TALER_GlobalFeeSet *fees,
- struct GNUNET_TIME_Relative purse_timeout,
- struct GNUNET_TIME_Relative history_expiration,
- uint32_t purse_account_limit,
- const struct TALER_MasterSignatureP *master_sig)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_timestamp (&start_date),
- GNUNET_PQ_query_param_timestamp (&end_date),
- TALER_PQ_query_param_amount (&fees->history),
- TALER_PQ_query_param_amount (&fees->account),
- TALER_PQ_query_param_amount (&fees->purse),
- GNUNET_PQ_query_param_relative_time (&purse_timeout),
- GNUNET_PQ_query_param_relative_time (&history_expiration),
- GNUNET_PQ_query_param_uint32 (&purse_account_limit),
- GNUNET_PQ_query_param_auto_from_type (master_sig),
- GNUNET_PQ_query_param_end
- };
- struct TALER_GlobalFeeSet wx;
- struct TALER_MasterSignatureP sig;
- struct GNUNET_TIME_Timestamp sd;
- struct GNUNET_TIME_Timestamp ed;
- enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_TIME_Relative pt;
- struct GNUNET_TIME_Relative he;
- uint32_t pal;
-
- qs = postgres_get_global_fee (pg,
- start_date,
- &sd,
- &ed,
- &wx,
- &pt,
- &he,
- &pal,
- &sig);
- if (qs < 0)
- return qs;
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
- {
- if (0 != GNUNET_memcmp (&sig,
- master_sig))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- if (0 !=
- TALER_global_fee_set_cmp (fees,
- &wx))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- if ( (GNUNET_TIME_timestamp_cmp (sd,
- !=,
- start_date)) ||
- (GNUNET_TIME_timestamp_cmp (ed,
- !=,
- end_date)) )
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- if ( (GNUNET_TIME_relative_cmp (purse_timeout,
- !=,
- pt)) ||
- (GNUNET_TIME_relative_cmp (history_expiration,
- !=,
- he)) )
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- if (purse_account_limit != pal)
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- /* equal record already exists */
- return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
- }
-
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_global_fee",
- params);
-}
-
-
-/**
- * Insert reserve close operation into database.
- *
- * @param cls closure
- * @param reserve_pub which reserve is this about?
- * @param execution_date when did we perform the transfer?
- * @param receiver_account to which account do we transfer?
- * @param wtid wire transfer details
- * @param amount_with_fee amount we charged to the reserve
- * @param closing_fee how high is the closing fee
- * @param close_request_row identifies explicit close request, 0 for none
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_reserve_closed (
- void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- struct GNUNET_TIME_Timestamp execution_date,
- const char *receiver_account,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- const struct TALER_Amount *amount_with_fee,
- const struct TALER_Amount *closing_fee,
- uint64_t close_request_row)
-{
- struct PostgresClosure *pg = cls;
- struct TALER_EXCHANGEDB_Reserve reserve;
- enum GNUNET_DB_QueryStatus qs;
- struct TALER_PaytoHashP h_payto;
-
- TALER_payto_hash (receiver_account,
- &h_payto);
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- GNUNET_PQ_query_param_timestamp (&execution_date),
- GNUNET_PQ_query_param_auto_from_type (wtid),
- GNUNET_PQ_query_param_auto_from_type (&h_payto),
- TALER_PQ_query_param_amount (amount_with_fee),
- TALER_PQ_query_param_amount (closing_fee),
- GNUNET_PQ_query_param_uint64 (&close_request_row),
- GNUNET_PQ_query_param_end
- };
-
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "reserves_close_insert",
- params);
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- return qs;
-
- /* update reserve balance */
- reserve.pub = *reserve_pub;
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- (qs = TEH_PG_reserves_get (cls,
- &reserve)))
- {
- /* Existence should have been checked before we got here... */
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- qs = GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
- }
- {
- enum TALER_AmountArithmeticResult ret;
-
- ret = TALER_amount_subtract (&reserve.balance,
- &reserve.balance,
- amount_with_fee);
- if (ret < 0)
- {
- /* The reserve history was checked to make sure there is enough of a balance
- left before we tried this; however, concurrent operations may have changed
- the situation by now. We should re-try the transaction. */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Closing of reserve `%s' refused due to balance mismatch. Retrying.\n",
- TALER_B2S (reserve_pub));
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- GNUNET_break (TALER_AAR_RESULT_ZERO == ret);
- }
- return TEH_PG_reserves_update (cls,
- &reserve);
-}
-
-
-/**
- * Function called to insert wire transfer commit data into the DB.
- *
- * @param cls closure
- * @param type type of the wire transfer (i.e. "iban")
- * @param buf buffer with wire transfer preparation data
- * @param buf_size number of bytes in @a buf
- * @return query status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_wire_prepare_data_insert (void *cls,
- const char *type,
- const char *buf,
- size_t buf_size)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_string (type),
- GNUNET_PQ_query_param_fixed_size (buf, buf_size),
- GNUNET_PQ_query_param_end
- };
-
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "wire_prepare_data_insert",
- params);
-}
-
-
-/**
- * Function called to mark wire transfer commit data as finished.
- *
- * @param cls closure
- * @param rowid which entry to mark as finished
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_wire_prepare_data_mark_finished (
- void *cls,
- uint64_t rowid)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&rowid),
- GNUNET_PQ_query_param_end
- };
-
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "wire_prepare_data_mark_done",
- params);
-}
-
-
-/**
- * Function called to mark wire transfer commit data as failed.
- *
- * @param cls closure
- * @param rowid which entry to mark as failed
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_wire_prepare_data_mark_failed (
- void *cls,
- uint64_t rowid)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&rowid),
- GNUNET_PQ_query_param_end
- };
-
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "wire_prepare_data_mark_failed",
- params);
-}
-
-
-/**
- * Closure for #prewire_cb().
- */
-struct PrewireContext
-{
- /**
- * Function to call on each result.
- */
- TALER_EXCHANGEDB_WirePreparationIterator cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * #GNUNET_OK if everything went fine.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Invoke the callback for each result.
- *
- * @param cls a `struct MissingWireContext *`
- * @param result SQL result
- * @param num_results number of rows in @a result
- */
-static void
-prewire_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct PrewireContext *pc = cls;
-
- for (unsigned int i = 0; i < num_results; i++)
- {
- uint64_t prewire_uuid;
- char *wire_method;
- void *buf = NULL;
- size_t buf_size;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("prewire_uuid",
- &prewire_uuid),
- GNUNET_PQ_result_spec_string ("wire_method",
- &wire_method),
- GNUNET_PQ_result_spec_variable_size ("buf",
- &buf,
- &buf_size),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- pc->status = GNUNET_SYSERR;
- return;
- }
- pc->cb (pc->cb_cls,
- prewire_uuid,
- wire_method,
- buf,
- buf_size);
- GNUNET_PQ_cleanup_result (rs);
- }
-}
-
-
-/**
- * Function called to get an unfinished wire transfer
- * preparation data. Fetches at most one item.
- *
- * @param cls closure
- * @param start_row offset to query table at
- * @param limit maximum number of results to return
- * @param cb function to call for ONE unfinished item
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_wire_prepare_data_get (void *cls,
- uint64_t start_row,
- uint64_t limit,
- TALER_EXCHANGEDB_WirePreparationIterator cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&start_row),
- GNUNET_PQ_query_param_uint64 (&limit),
- GNUNET_PQ_query_param_end
- };
- struct PrewireContext pc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "wire_prepare_data_get",
- params,
- &prewire_cb,
- &pc);
- if (GNUNET_OK != pc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Starts a READ COMMITTED transaction where we transiently violate the foreign
- * constraints on the "wire_out" table as we insert aggregations
- * and only add the wire transfer out at the end.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-postgres_start_deferred_wire_out (void *cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_ExecuteStatement es[] = {
- GNUNET_PQ_make_execute (
- "START TRANSACTION ISOLATION LEVEL READ COMMITTED;"),
- GNUNET_PQ_make_execute ("SET CONSTRAINTS ALL DEFERRED;"),
- GNUNET_PQ_EXECUTE_STATEMENT_END
- };
-
- if (GNUNET_SYSERR ==
- TEH_PG_preflight (pg))
- return GNUNET_SYSERR;
- if (GNUNET_OK !=
- GNUNET_PQ_exec_statements (pg->conn,
- es))
- {
- TALER_LOG_ERROR (
- "Failed to defer wire_out_ref constraint on transaction\n");
- GNUNET_break (0);
- TEH_PG_rollback (pg);
- return GNUNET_SYSERR;
- }
- pg->transaction_name = "deferred wire out";
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Starting READ COMMITTED DEFERRED transaction `%s'\n",
- pg->transaction_name);
- return GNUNET_OK;
-}
-
-
-/**
- * Store information about an outgoing wire transfer that was executed.
- *
- * @param cls closure
- * @param date time of the wire transfer
- * @param wtid subject of the wire transfer
- * @param h_payto identifies the receiver account of the wire transfer
- * @param exchange_account_section configuration section of the exchange specifying the
- * exchange's bank account being used
- * @param amount amount that was transmitted
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_store_wire_transfer_out (
- void *cls,
- struct GNUNET_TIME_Timestamp date,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- const struct TALER_PaytoHashP *h_payto,
- const char *exchange_account_section,
- const struct TALER_Amount *amount)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_timestamp (&date),
- GNUNET_PQ_query_param_auto_from_type (wtid),
- GNUNET_PQ_query_param_auto_from_type (h_payto),
- GNUNET_PQ_query_param_string (exchange_account_section),
- TALER_PQ_query_param_amount (amount),
- GNUNET_PQ_query_param_end
- };
-
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_wire_out",
- params);
-}
-
-
-/**
- * Function called to perform "garbage collection" on the
- * database, expiring records we no longer require.
- *
- * @param cls closure
- * @return #GNUNET_OK on success,
- * #GNUNET_SYSERR on DB errors
- */
-static enum GNUNET_GenericReturnValue
-postgres_gc (void *cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
- struct GNUNET_TIME_Absolute long_ago;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_absolute_time (&long_ago),
- GNUNET_PQ_query_param_absolute_time (&now),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_Context *conn;
- enum GNUNET_GenericReturnValue ret;
-
- /* Keep wire fees for 10 years, that should always
- be enough _and_ they are tiny so it does not
- matter to make this tight */
- long_ago = GNUNET_TIME_absolute_subtract (
- now,
- GNUNET_TIME_relative_multiply (
- GNUNET_TIME_UNIT_YEARS,
- 10));
- {
- struct GNUNET_PQ_ExecuteStatement es[] = {
- GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"),
- GNUNET_PQ_EXECUTE_STATEMENT_END
- };
- struct GNUNET_PQ_PreparedStatement ps[] = {
- /* Used in #postgres_gc() */
- GNUNET_PQ_make_prepare ("run_gc",
- "CALL"
- " exchange_do_gc"
- " ($1,$2);"),
- GNUNET_PQ_PREPARED_STATEMENT_END
- };
-
- conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
- "exchangedb-postgres",
- NULL,
- es,
- ps);
- }
- if (NULL == conn)
- return GNUNET_SYSERR;
- ret = GNUNET_OK;
- if (0 > GNUNET_PQ_eval_prepared_non_select (conn,
- "run_gc",
- params))
- ret = GNUNET_SYSERR;
- GNUNET_PQ_disconnect (conn);
- return ret;
-}
-
-
-/**
- * Closure for #deposit_serial_helper_cb().
- */
-struct DepositSerialContext
-{
-
- /**
- * Callback to call.
- */
- TALER_EXCHANGEDB_DepositCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Status code, set to #GNUNET_SYSERR on hard errors.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Helper function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct DepositSerialContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-deposit_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct DepositSerialContext *dsc = cls;
- struct PostgresClosure *pg = dsc->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
- ret = dsc->cb (dsc->cb_cls,
- rowid,
- exchange_timestamp,
- &deposit,
- &denom_pub,
- done);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
- }
-}
-
-
-/**
- * Select deposits above @a serial_id in monotonically increasing
- * order.
- *
- * @param cls closure
- * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call on each result
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_deposits_above_serial_id (
- void *cls,
- uint64_t serial_id,
- TALER_EXCHANGEDB_DepositCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_end
- };
- struct DepositSerialContext dsc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_get_deposits_incr",
- params,
- &deposit_serial_helper_cb,
- &dsc);
- if (GNUNET_OK != dsc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Closure for #purse_deposit_serial_helper_cb().
- */
-struct HistoryRequestSerialContext
-{
-
- /**
- * Callback to call.
- */
- TALER_EXCHANGEDB_HistoryRequestCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Status code, set to #GNUNET_SYSERR on hard errors.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Helper function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct HistoryRequestSerialContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-history_request_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct HistoryRequestSerialContext *dsc = cls;
- struct PostgresClosure *pg = dsc->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
- ret = dsc->cb (dsc->cb_cls,
- rowid,
- &history_fee,
- ts,
- &reserve_pub,
- &reserve_sig);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
- }
-}
-
-
-/**
- * Select history requests above @a serial_id in monotonically increasing
- * order.
- *
- * @param cls closure
- * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call on each result
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_history_requests_above_serial_id (
- void *cls,
- uint64_t serial_id,
- TALER_EXCHANGEDB_HistoryRequestCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_end
- };
- struct HistoryRequestSerialContext dsc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_get_history_requests_incr",
- params,
- &history_request_serial_helper_cb,
- &dsc);
- if (GNUNET_OK != dsc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Closure for #purse_decision_serial_helper_cb().
- */
-struct PurseDecisionSerialContext
-{
-
- /**
- * Callback to call.
- */
- TALER_EXCHANGEDB_PurseDecisionCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Status code, set to #GNUNET_SYSERR on hard errors.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Helper function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct PurseRefundSerialContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-purse_decision_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct PurseDecisionSerialContext *dsc = cls;
- struct PostgresClosure *pg = dsc->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
- ret = dsc->cb (dsc->cb_cls,
- rowid,
- &purse_pub,
- no_reserve ? NULL : &reserve_pub,
- &val);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
- }
-}
-
-
-/**
- * Select purse decisions above @a serial_id in monotonically increasing
- * order.
- *
- * @param cls closure
- * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param refunded which refund status to select for
- * @param cb function to call on each result
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_purse_decisions_above_serial_id (
- void *cls,
- uint64_t serial_id,
- bool refunded,
- TALER_EXCHANGEDB_PurseDecisionCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_bool (refunded),
- GNUNET_PQ_query_param_end
- };
- struct PurseDecisionSerialContext dsc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_get_purse_decisions_incr",
- params,
- &purse_decision_serial_helper_cb,
- &dsc);
- if (GNUNET_OK != dsc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Closure for #purse_refund_coin_helper_cb().
- */
-struct PurseRefundCoinContext
-{
-
- /**
- * Callback to call.
- */
- TALER_EXCHANGEDB_PurseRefundCoinCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Status code, set to #GNUNET_SYSERR on hard errors.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Helper function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct PurseRefundCoinContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-purse_refund_coin_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct PurseRefundCoinContext *dsc = cls;
- struct PostgresClosure *pg = dsc->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
- ret = dsc->cb (dsc->cb_cls,
- rowid,
- &amount_with_fee,
- &coin_pub,
- &denom_pub);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
- }
-}
-
-
-/**
- * Select coin affected by purse refund.
- *
- * @param cls closure
- * @param purse_pub purse that was refunded
- * @param cb function to call on each result
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_purse_deposits_by_purse (
- void *cls,
- const struct TALER_PurseContractPublicKeyP *purse_pub,
- TALER_EXCHANGEDB_PurseRefundCoinCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (purse_pub),
- GNUNET_PQ_query_param_end
- };
- struct PurseRefundCoinContext dsc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_get_purse_deposits_by_purse",
- params,
- &purse_refund_coin_helper_cb,
- &dsc);
- if (GNUNET_OK != dsc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Closure for #refreshs_serial_helper_cb().
- */
-struct RefreshsSerialContext
-{
-
- /**
- * Callback to call.
- */
- TALER_EXCHANGEDB_RefreshesCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Status code, set to #GNUNET_SYSERR on hard errors.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Helper function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct RefreshsSerialContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-refreshs_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct RefreshsSerialContext *rsc = cls;
- struct PostgresClosure *pg = rsc->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
-
- ret = rsc->cb (rsc->cb_cls,
- rowid,
- &denom_pub,
- ac_isnull ? NULL : &h_age_commitment,
- &coin_pub,
- &coin_sig,
- &amount_with_fee,
- noreveal_index,
- &rc);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
- }
-}
-
-
-/**
- * Select refresh sessions above @a serial_id in monotonically increasing
- * order.
- *
- * @param cls closure
- * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call on each result
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_refreshes_above_serial_id (
- void *cls,
- uint64_t serial_id,
- TALER_EXCHANGEDB_RefreshesCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_end
- };
- struct RefreshsSerialContext rsc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_get_refresh_commitments_incr",
- params,
- &refreshs_serial_helper_cb,
- &rsc);
- if (GNUNET_OK != rsc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Closure for #refunds_serial_helper_cb().
- */
-struct RefundsSerialContext
-{
-
- /**
- * Callback to call.
- */
- TALER_EXCHANGEDB_RefundCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Status code, set to #GNUNET_SYSERR on hard errors.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Helper function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct RefundsSerialContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-refunds_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct RefundsSerialContext *rsc = cls;
- struct PostgresClosure *pg = rsc->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&rowid),
- GNUNET_PQ_query_param_end
- };
- struct TALER_Amount amount_with_fee;
- uint64_t s_f;
- uint64_t s_v;
- struct GNUNET_PQ_ResultSpec rs2[] = {
- GNUNET_PQ_result_spec_uint64 ("s_v",
- &s_v),
- GNUNET_PQ_result_spec_uint64 ("s_f",
- &s_f),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- &amount_with_fee),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_singleton_select (
- pg->conn,
- "test_refund_full",
- params,
- rs2);
- if (qs <= 0)
- {
- GNUNET_break (0);
- rsc->status = GNUNET_SYSERR;
- return;
- }
- /* normalize */
- s_v += s_f / TALER_AMOUNT_FRAC_BASE;
- s_f %= TALER_AMOUNT_FRAC_BASE;
- full_refund = (s_v >= amount_with_fee.value) &&
- (s_f >= amount_with_fee.fraction);
- }
- ret = rsc->cb (rsc->cb_cls,
- rowid,
- &denom_pub,
- &refund.coin.coin_pub,
- &refund.details.merchant_pub,
- &refund.details.merchant_sig,
- &refund.details.h_contract_terms,
- refund.details.rtransaction_id,
- full_refund,
- &refund.details.refund_amount);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
- }
-}
-
-
-/**
- * Select refunds above @a serial_id in monotonically increasing
- * order.
- *
- * @param cls closure
- * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call on each result
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_refunds_above_serial_id (
- void *cls,
- uint64_t serial_id,
- TALER_EXCHANGEDB_RefundCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_end
- };
- struct RefundsSerialContext rsc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_get_refunds_incr",
- params,
- &refunds_serial_helper_cb,
- &rsc);
- if (GNUNET_OK != rsc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Closure for #reserves_in_serial_helper_cb().
- */
-struct ReservesInSerialContext
-{
-
- /**
- * Callback to call.
- */
- TALER_EXCHANGEDB_ReserveInCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Status code, set to #GNUNET_SYSERR on hard errors.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Helper function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct ReservesInSerialContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-reserves_in_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct ReservesInSerialContext *risc = cls;
- struct PostgresClosure *pg = risc->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
- ret = risc->cb (risc->cb_cls,
- rowid,
- &reserve_pub,
- &credit,
- sender_account_details,
- wire_reference,
- execution_date);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
- }
-}
-
-
-/**
- * Select inbound wire transfers into reserves_in above @a serial_id
- * in monotonically increasing order.
- *
- * @param cls closure
- * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call on each result
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_reserves_in_above_serial_id (
- void *cls,
- uint64_t serial_id,
- TALER_EXCHANGEDB_ReserveInCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_end
- };
- struct ReservesInSerialContext risc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_reserves_in_get_transactions_incr",
- params,
- &reserves_in_serial_helper_cb,
- &risc);
- if (GNUNET_OK != risc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Select inbound wire transfers into reserves_in above @a serial_id
- * in monotonically increasing order by account.
- *
- * @param cls closure
- * @param account_name name of the account to select by
- * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call on each result
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_reserves_in_above_serial_id_by_account (
- void *cls,
- const char *account_name,
- uint64_t serial_id,
- TALER_EXCHANGEDB_ReserveInCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_string (account_name),
- GNUNET_PQ_query_param_end
- };
- struct ReservesInSerialContext risc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_reserves_in_get_transactions_incr_by_account",
- params,
- &reserves_in_serial_helper_cb,
- &risc);
- if (GNUNET_OK != risc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Closure for #reserves_out_serial_helper_cb().
- */
-struct ReservesOutSerialContext
-{
-
- /**
- * Callback to call.
- */
- TALER_EXCHANGEDB_WithdrawCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Status code, set to #GNUNET_SYSERR on hard errors.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Helper function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct ReservesOutSerialContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-reserves_out_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct ReservesOutSerialContext *rosc = cls;
- struct PostgresClosure *pg = rosc->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
- ret = rosc->cb (rosc->cb_cls,
- rowid,
- &h_blind_ev,
- &denom_pub,
- &reserve_pub,
- &reserve_sig,
- execution_date,
- &amount_with_fee);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
- }
-}
-
-
-/**
- * Select withdraw operations from reserves_out above @a serial_id
- * in monotonically increasing order.
- *
- * @param cls closure
- * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call on each result
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_withdrawals_above_serial_id (
- void *cls,
- uint64_t serial_id,
- TALER_EXCHANGEDB_WithdrawCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_end
- };
- struct ReservesOutSerialContext rosc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_get_reserves_out_incr",
- params,
- &reserves_out_serial_helper_cb,
- &rosc);
- if (GNUNET_OK != rosc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Closure for #wire_out_serial_helper_cb().
- */
-struct WireOutSerialContext
-{
-
- /**
- * Callback to call.
- */
- TALER_EXCHANGEDB_WireTransferOutCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Status code, set to #GNUNET_SYSERR on hard errors.
- */
- int status;
-};
-
-
-/**
- * Helper function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct WireOutSerialContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-wire_out_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct WireOutSerialContext *wosc = cls;
- struct PostgresClosure *pg = wosc->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
- ret = wosc->cb (wosc->cb_cls,
- rowid,
- date,
- &wtid,
- payto_uri,
- &amount);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
- }
-}
-
-
-/**
- * Function called to select all wire transfers the exchange
- * executed.
- *
- * @param cls closure
- * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call for ONE unfinished item
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_wire_out_above_serial_id (
- void *cls,
- uint64_t serial_id,
- TALER_EXCHANGEDB_WireTransferOutCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_end
- };
- struct WireOutSerialContext wosc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_get_wire_incr",
- params,
- &wire_out_serial_helper_cb,
- &wosc);
- if (GNUNET_OK != wosc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Function called to select all wire transfers the exchange
- * executed by account.
- *
- * @param cls closure
- * @param account_name account to select
- * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call for ONE unfinished item
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_wire_out_above_serial_id_by_account (
- void *cls,
- const char *account_name,
- uint64_t serial_id,
- TALER_EXCHANGEDB_WireTransferOutCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_string (account_name),
- GNUNET_PQ_query_param_end
- };
- struct WireOutSerialContext wosc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_get_wire_incr_by_account",
- params,
- &wire_out_serial_helper_cb,
- &wosc);
- if (GNUNET_OK != wosc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Closure for #recoup_serial_helper_cb().
- */
-struct RecoupSerialContext
-{
-
- /**
- * Callback to call.
- */
- TALER_EXCHANGEDB_RecoupCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Status code, set to #GNUNET_SYSERR on hard errors.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Helper function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct RecoupSerialContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-recoup_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct RecoupSerialContext *psc = cls;
- struct PostgresClosure *pg = psc->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
- ret = psc->cb (psc->cb_cls,
- rowid,
- timestamp,
- &amount,
- &reserve_pub,
- &coin,
- &denom_pub,
- &coin_sig,
- &coin_blind);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
- }
-}
-
-
-/**
- * Function called to select recoup requests the exchange
- * received, ordered by serial ID (monotonically increasing).
- *
- * @param cls closure
- * @param serial_id lowest serial ID to include (select larger or equal)
- * @param cb function to call for ONE unfinished item
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_recoup_above_serial_id (
- void *cls,
- uint64_t serial_id,
- TALER_EXCHANGEDB_RecoupCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_end
- };
- struct RecoupSerialContext psc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "recoup_get_incr",
- params,
- &recoup_serial_helper_cb,
- &psc);
- if (GNUNET_OK != psc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Closure for #recoup_refresh_serial_helper_cb().
- */
-struct RecoupRefreshSerialContext
-{
-
- /**
- * Callback to call.
- */
- TALER_EXCHANGEDB_RecoupRefreshCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Status code, set to #GNUNET_SYSERR on hard errors.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Helper function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct RecoupRefreshSerialContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-recoup_refresh_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct RecoupRefreshSerialContext *psc = cls;
- struct PostgresClosure *pg = psc->pg;
-
- for (unsigned int i = 0; istatus = GNUNET_SYSERR;
- return;
- }
- ret = psc->cb (psc->cb_cls,
- rowid,
- timestamp,
- &amount,
- &old_coin_pub,
- &old_denom_pub_hash,
- &coin,
- &denom_pub,
- &coin_sig,
- &coin_blind);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
- }
-}
-
-
-/**
- * Function called to select recoup requests the exchange received for
- * refreshed coins, ordered by serial ID (monotonically increasing).
- *
- * @param cls closure
- * @param serial_id lowest serial ID to include (select larger or equal)
- * @param cb function to call for ONE unfinished item
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_recoup_refresh_above_serial_id (
- void *cls,
- uint64_t serial_id,
- TALER_EXCHANGEDB_RecoupRefreshCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_end
- };
- struct RecoupRefreshSerialContext psc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "recoup_refresh_get_incr",
- params,
- &recoup_refresh_serial_helper_cb,
- &psc);
- if (GNUNET_OK != psc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
- * Obtain information about which reserve a coin was generated
- * from given the hash of the blinded coin.
- *
- * @param cls closure
- * @param bch hash that uniquely identifies the withdraw request
- * @param[out] reserve_pub set to information about the reserve (on success only)
- * @param[out] reserve_out_serial_id set to row of the @a h_blind_ev in reserves_out
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_reserve_by_h_blind (
- void *cls,
- const struct TALER_BlindedCoinHashP *bch,
- struct TALER_ReservePublicKeyP *reserve_pub,
- uint64_t *reserve_out_serial_id)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (bch),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
- reserve_pub),
- GNUNET_PQ_result_spec_uint64 ("reserve_out_serial_id",
- reserve_out_serial_id),
- GNUNET_PQ_result_spec_end
- };
-
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "reserve_by_h_blind",
- params,
- rs);
-}
-
-
/**
* Initialize Postgres database subsystem.
*
@@ -4960,81 +483,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
}
plugin = GNUNET_new (struct TALER_EXCHANGEDB_Plugin);
plugin->cls = pg;
- plugin->get_policy_details = &postgres_get_policy_details;
- plugin->add_policy_fulfillment_proof = &postgres_add_policy_fulfillment_proof;
- plugin->do_melt = &postgres_do_melt;
- plugin->do_refund = &postgres_do_refund;
- plugin->do_recoup = &postgres_do_recoup;
- plugin->do_recoup_refresh = &postgres_do_recoup_refresh;
- plugin->get_reserve_balance = &postgres_get_reserve_balance;
- plugin->count_known_coins = &postgres_count_known_coins;
- plugin->ensure_coin_known = &postgres_ensure_coin_known;
- plugin->get_known_coin = &postgres_get_known_coin;
- plugin->get_coin_denomination = &postgres_get_coin_denomination;
- plugin->have_deposit2 = &postgres_have_deposit2;
- plugin->aggregate = &postgres_aggregate;
- plugin->create_aggregation_transient
- = &postgres_create_aggregation_transient;
- plugin->select_aggregation_transient
- = &postgres_select_aggregation_transient;
- plugin->find_aggregation_transient
- = &postgres_find_aggregation_transient;
- plugin->update_aggregation_transient
- = &postgres_update_aggregation_transient;
- plugin->get_ready_deposit = &postgres_get_ready_deposit;
- plugin->insert_deposit = &postgres_insert_deposit;
- plugin->insert_refund = &postgres_insert_refund;
- plugin->select_refunds_by_coin = &postgres_select_refunds_by_coin;
- plugin->get_melt = &postgres_get_melt;
- plugin->insert_refresh_reveal = &postgres_insert_refresh_reveal;
- plugin->get_refresh_reveal = &postgres_get_refresh_reveal;
- plugin->lookup_wire_transfer = &postgres_lookup_wire_transfer;
- plugin->lookup_transfer_by_deposit = &postgres_lookup_transfer_by_deposit;
- plugin->insert_wire_fee = &postgres_insert_wire_fee;
- plugin->insert_global_fee = &postgres_insert_global_fee;
- plugin->get_wire_fee = &postgres_get_wire_fee;
- plugin->get_global_fee = &postgres_get_global_fee;
- plugin->get_global_fees = &postgres_get_global_fees;
- plugin->insert_reserve_closed = &postgres_insert_reserve_closed;
- plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert;
- plugin->wire_prepare_data_mark_finished =
- &postgres_wire_prepare_data_mark_finished;
- plugin->wire_prepare_data_mark_failed =
- &postgres_wire_prepare_data_mark_failed;
- plugin->wire_prepare_data_get = &postgres_wire_prepare_data_get;
- plugin->start_deferred_wire_out = &postgres_start_deferred_wire_out;
- plugin->store_wire_transfer_out = &postgres_store_wire_transfer_out;
- plugin->gc = &postgres_gc;
-
- plugin->select_deposits_above_serial_id
- = &postgres_select_deposits_above_serial_id;
- plugin->select_history_requests_above_serial_id
- = &postgres_select_history_requests_above_serial_id;
- plugin->select_purse_decisions_above_serial_id
- = &postgres_select_purse_decisions_above_serial_id;
- plugin->select_purse_deposits_by_purse
- = &postgres_select_purse_deposits_by_purse;
- plugin->select_refreshes_above_serial_id
- = &postgres_select_refreshes_above_serial_id;
- plugin->select_refunds_above_serial_id
- = &postgres_select_refunds_above_serial_id;
- plugin->select_reserves_in_above_serial_id
- = &postgres_select_reserves_in_above_serial_id;
- plugin->select_reserves_in_above_serial_id_by_account
- = &postgres_select_reserves_in_above_serial_id_by_account;
- plugin->select_withdrawals_above_serial_id
- = &postgres_select_withdrawals_above_serial_id;
- plugin->select_wire_out_above_serial_id
- = &postgres_select_wire_out_above_serial_id;
- plugin->select_wire_out_above_serial_id_by_account
- = &postgres_select_wire_out_above_serial_id_by_account;
- plugin->select_recoup_above_serial_id
- = &postgres_select_recoup_above_serial_id;
- plugin->select_recoup_refresh_above_serial_id
- = &postgres_select_recoup_refresh_above_serial_id;
- plugin->get_reserve_by_h_blind
- = &postgres_get_reserve_by_h_blind;
-
/* New style, sort alphabetically! */
plugin->do_reserve_open
= &TEH_PG_do_reserve_open;
@@ -5376,6 +824,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &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;
return plugin;
}
diff --git a/src/exchangedb/test_exchangedb_batch_reserves_in_insert.c b/src/exchangedb/test_exchangedb_batch_reserves_in_insert.c
new file mode 100644
index 000000000..460778b88
--- /dev/null
+++ b/src/exchangedb/test_exchangedb_batch_reserves_in_insert.c
@@ -0,0 +1,201 @@
+/*
+ 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
+*/
+/**
+ * @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))
+ {
+ GNUNET_break (0);
+ result = 77;
+ goto cleanup;
+ }
+ if (GNUNET_OK !=
+ plugin->setup_partitions (plugin->cls,
+ num_partitions))
+ {
+ GNUNET_break (0);
+ result = 77;
+ 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_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 r=0;r<10;r++)
+ {
+
+ for (unsigned int k = 0; kbatch_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 */
diff --git a/src/exchangedb/test_exchangedb_by_j.c b/src/exchangedb/test_exchangedb_by_j.c
index 43f471671..834373b55 100644
--- a/src/exchangedb/test_exchangedb_by_j.c
+++ b/src/exchangedb/test_exchangedb_by_j.c
@@ -91,9 +91,22 @@ run (void *cls)
result = 77;
goto cleanup;
}
+
for (unsigned int i = 0; i< 7; i++)
+
+ if (GNUNET_OK !=
+ plugin->setup_partitions (plugin->cls,
+ num_partitions))
{
- static unsigned int batches[] = {1, 1, 2, 4, 16, 64, 256};
+ GNUNET_break (0);
+ result = 77;
+ 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];
@@ -101,6 +114,7 @@ run (void *cls)
struct GNUNET_TIME_Timestamp ts;
struct GNUNET_TIME_Relative duration;
struct TALER_EXCHANGEDB_ReserveInInfo reserves[batch_size];
+ /* struct TALER_EXCHANGEDB_ReserveInInfo reserves2[batch_size];*/
enum GNUNET_DB_QueryStatus results[batch_size];
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":1.000010",
@@ -109,10 +123,8 @@ run (void *cls)
ts = GNUNET_TIME_timestamp_get ();
for (unsigned int r = 0; r<10; r++)
{
- plugin->start_read_committed (plugin->cls,
- "test_by_j");
- for (unsigned int k = 0; kbatch_reserves_in_insert (plugin->cls,
- reserves,
- batch_size,
- results));
-
- plugin->commit (plugin->cls);
+ 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:
@@ -155,7 +166,6 @@ main (int argc,
char *config_filename;
char *testname;
struct GNUNET_CONFIGURATION_Handle *cfg;
-
(void) argc;
result = -1;
if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
@@ -163,6 +173,7 @@ main (int argc,
GNUNET_break (0);
return -1;
}
+
GNUNET_log_setup (argv[0],
"WARNING",
NULL);
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index a2e3237f9..6951908ef 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -3453,6 +3453,21 @@ struct TALER_EXCHANGEDB_Plugin
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,
+ const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
+ unsigned int reserves_length,
+ enum GNUNET_DB_QueryStatus *results);
/**
* Locate a nonce for use with a particular public key.