aboutsummaryrefslogtreecommitdiff
path: root/src/exchangedb
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchangedb')
-rw-r--r--src/exchangedb/Makefile.am34
-rw-r--r--src/exchangedb/pg_batch2_reserves_in_insert.c253
-rw-r--r--src/exchangedb/pg_batch2_reserves_in_insert.h33
-rw-r--r--src/exchangedb/pg_batch_reserves_in_insert.c13
-rw-r--r--src/exchangedb/pg_get_link_data.c68
-rw-r--r--src/exchangedb/pg_select_refunds_by_coin.c20
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c4481
-rw-r--r--src/exchangedb/test_exchangedb_batch_reserves_in_insert.c201
-rw-r--r--src/exchangedb/test_exchangedb_by_j.c48
9 files changed, 600 insertions, 4551 deletions
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index 4d9bfcb59..307b5519f 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,17 @@ 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 +339,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..e4e015f96
--- /dev/null
+++ b/src/exchangedb/pg_batch2_reserves_in_insert.c
@@ -0,0 +1,253 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file exchangedb/pg_batch_reserves_in_insert.c
+ * @brief Implementation of the reserves_in_insert function for Postgres
+ * @author Joseph Xu
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_batch_reserves_in_insert.h"
+#include "pg_helper.h"
+#include "pg_start.h"
+#include "pg_rollback.h"
+#include "pg_start_read_committed.h"
+#include "pg_commit.h"
+#include "pg_reserves_get.h"
+#include "pg_reserves_update.h"
+#include "pg_setup_wire_target.h"
+#include "pg_event_notify.h"
+#include "pg_preflight.h"
+
+/**
+ * Generate event notification for the reserve change.
+ *
+ * @param reserve_pub reserve to notfiy on
+ */
+static char *
+compute_notify_on_reserve (const struct TALER_ReservePublicKeyP *reserve_pub)
+{
+ struct TALER_ReserveEventP rep = {
+ .header.size = htons (sizeof (rep)),
+ .header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
+ .reserve_pub = *reserve_pub
+ };
+
+ return GNUNET_PG_get_event_notify_channel (&rep.header);
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_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;
+ 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 batch2_reserves_insert"
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17);");
+ expiry = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (reserves->execution_time.abs_time,
+ pg->idle_reserve_expiration_time));
+ gc = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
+ pg->legal_reserve_expiration_time));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Creating reserve %s with expiration in %s\n",
+ TALER_B2S (&(reserves->reserve_pub)),
+ GNUNET_STRINGS_relative_time_to_string (
+ pg->idle_reserve_expiration_time,
+ GNUNET_NO));
+
+ {
+ if (GNUNET_OK !=
+ TEH_PG_start_read_committed(pg,
+ "READ_COMMITED"))
+ {
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ }
+ /* Optimistically assume this is a new reserve, create balance for the first
+ time; we do this before adding the actual transaction to "reserves_in",
+ as for a new reserve it can't be a duplicate 'add' operation, and as
+ the 'add' operation needs the reserve entry as a foreign key. */
+ for (unsigned int i=0;i<reserves_length;i++)
+ {
+ const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
+ notify_s[i] = compute_notify_on_reserve (&reserve->reserve_pub);
+ }
+
+ for (unsigned int i=0;i<reserves_length;i++)
+ {
+ const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&reserve->reserve_pub),
+ GNUNET_PQ_query_param_timestamp (&expiry),
+ GNUNET_PQ_query_param_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),
+ TALER_PQ_query_param_amount (&reserve->balance),
+ GNUNET_PQ_query_param_timestamp (&expiry),
+ GNUNET_PQ_query_param_timestamp (&gc),
+ GNUNET_PQ_query_param_end
+ };
+
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("conflicted",
+ &conflicted),
+ GNUNET_PQ_result_spec_bool ("transaction_duplicate",
+ &transaction_duplicate),
+ GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
+ &reserve_uuid),
+ GNUNET_PQ_result_spec_end
+ };
+
+ TALER_payto_hash (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;
+ // 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;i<reserves_length;i++)
+ {
+ if (! conflicts[i])
+ continue;
+ {
+ const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&reserve->reserve_pub),
+ GNUNET_PQ_query_param_timestamp (&expiry),
+ GNUNET_PQ_query_param_uint64 (&reserve->wire_reference),
+ TALER_PQ_query_param_amount (&reserve->balance),
+ GNUNET_PQ_query_param_string (reserve->exchange_account_name),
+ GNUNET_PQ_query_param_bool (conflicted),
+ GNUNET_PQ_query_param_auto_from_type (&h_payto),
+ GNUNET_PQ_query_param_string (notify_s[i]),
+ GNUNET_PQ_query_param_end
+ };
+
+ 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<reserves_length;i++)
+ GNUNET_free (notify_s[i]);
+
+ return reserves_length;
+}
diff --git a/src/exchangedb/pg_batch2_reserves_in_insert.h b/src/exchangedb/pg_batch2_reserves_in_insert.h
new file mode 100644
index 000000000..bb6be3f6b
--- /dev/null
+++ b/src/exchangedb/pg_batch2_reserves_in_insert.h
@@ -0,0 +1,33 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file exchangedb/pg_batch2_reserves_in_insert.h
+ * @brief implementation of the batch2_reserves_in_insert function for Postgres
+ * @author 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_batch_reserves_in_insert.c b/src/exchangedb/pg_batch_reserves_in_insert.c
index f40641edd..2e6487516 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 Joseph XU
+ * @author Joseph Xu
*/
#include "platform.h"
#include "taler_error_codes.h"
@@ -32,6 +32,7 @@
#include "pg_reserves_update.h"
#include "pg_setup_wire_target.h"
#include "pg_event_notify.h"
+#include "pg_preflight.h"
/**
@@ -73,6 +74,12 @@ TEH_PG_batch_reserves_in_insert (void *cls,
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 "
@@ -143,11 +150,13 @@ TEH_PG_batch_reserves_in_insert (void *cls,
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,
@@ -160,6 +169,7 @@ TEH_PG_batch_reserves_in_insert (void *cls,
? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
: GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
conflicts[i] = conflicted;
+ // fprintf(stdout, "%d", conflicts[i]);
if (!conflicts[i] && transaction_duplicate)
{
GNUNET_break (0);
@@ -212,6 +222,7 @@ TEH_PG_batch_reserves_in_insert (void *cls,
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);
diff --git a/src/exchangedb/pg_get_link_data.c b/src/exchangedb/pg_get_link_data.c
index 930862890..f15bf35a2 100644
--- a/src/exchangedb/pg_get_link_data.c
+++ b/src/exchangedb/pg_get_link_data.c
@@ -178,25 +178,55 @@ TEH_PG_get_link_data (void *cls,
enum GNUNET_DB_QueryStatus qs;
struct LinkDataContext ldctx;
- PREPARE (pg,
- "get_link",
- "SELECT "
- " tp.transfer_pub"
- ",denoms.denom_pub"
- ",rrc.ev_sig"
- ",rrc.ewv"
- ",rrc.link_sig"
- ",rrc.freshcoin_index"
- ",rrc.coin_ev"
- " FROM refresh_commitments"
- " JOIN refresh_revealed_coins rrc"
- " USING (melt_serial_id)"
- " JOIN refresh_transfer_keys tp"
- " USING (melt_serial_id)"
- " JOIN denominations denoms"
- " ON (rrc.denominations_serial = denoms.denominations_serial)"
- " WHERE old_coin_pub=$1"
- " ORDER BY tp.transfer_pub, rrc.freshcoin_index ASC");
+ if (NULL == getenv ("NEW_LOGIC"))
+ {
+ PREPARE (pg,
+ "get_link",
+ "SELECT "
+ " tp.transfer_pub"
+ ",denoms.denom_pub"
+ ",rrc.ev_sig"
+ ",rrc.ewv"
+ ",rrc.link_sig"
+ ",rrc.freshcoin_index"
+ ",rrc.coin_ev"
+ " FROM refresh_commitments"
+ " JOIN refresh_revealed_coins rrc"
+ " USING (melt_serial_id)"
+ " JOIN refresh_transfer_keys tp"
+ " USING (melt_serial_id)"
+ " JOIN denominations denoms"
+ " ON (rrc.denominations_serial = denoms.denominations_serial)"
+ " WHERE old_coin_pub=$1"
+ " ORDER BY tp.transfer_pub, rrc.freshcoin_index ASC");
+ }
+
+ else
+ {
+ PREPARE (pg,
+ "get_link",
+ "WITH rc AS MATERIALIZED ("
+ "SELECT"
+ "* FROM refresh_commitments"
+ "WHERE old_coin_pub=$1"
+ ")"
+ "SELECT "
+ " tp.transfer_pub"
+ ",denoms.denom_pub"
+ ",rrc.ev_sig"
+ ",rrc.ewv"
+ ",rrc.link_sig"
+ ",rrc.freshcoin_index"
+ ",rrc.coin_ev"
+ " FROM refresh_revealed_coins rrc"
+ " USING (melt_serial_id)"
+ " JOIN refresh_transfer_keys tp"
+ " USING (melt_serial_id)"
+ " JOIN denominations denoms"
+ " USING (denominations_serial)"
+ " ORDER BY tp.transfer_pub, rrc.freshcoin_index ASC");
+ }
+
ldctx.ldc = ldc;
ldctx.ldc_cls = ldc_cls;
ldctx.last = NULL;
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..30aac29ac 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -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
@@ -374,4484 +375,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; i<num_results; i++)
- {
- struct TALER_Amount amount_with_fee;
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- &amount_with_fee),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- srctx->status = 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; i<num_results; i++)
- {
- struct TALER_Amount amount;
- char *payto_uri;
- struct TALER_WireTransferIdentifierRawP wtid;
- struct TALER_MerchantPublicKeyP merchant_pub;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
- &merchant_pub),
- GNUNET_PQ_result_spec_auto_from_type ("wtid_raw",
- &wtid),
- GNUNET_PQ_result_spec_string ("payto_uri",
- &payto_uri),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
- &amount),
- GNUNET_PQ_result_spec_end
- };
- bool cont;
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- srctx->status = 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; i<num_rrcs; i++)
- {
- const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&melt_serial_id),
- GNUNET_PQ_query_param_uint32 (&i),
- GNUNET_PQ_query_param_auto_from_type (&rrc->orig_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; i<num_results; i++)
- {
- uint64_t rowid;
- struct TALER_PrivateContractHashP h_contract_terms;
- struct TALER_CoinSpendPublicKeyP coin_pub;
- struct TALER_PaytoHashP h_payto;
- struct TALER_MerchantPublicKeyP merchant_pub;
- struct GNUNET_TIME_Timestamp exec_time;
- struct TALER_Amount amount_with_fee;
- struct TALER_Amount deposit_fee;
- struct TALER_DenominationPublicKey denom_pub;
- char *payto_uri;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("aggregation_serial_id", &rowid),
- GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
- &h_contract_terms),
- GNUNET_PQ_result_spec_string ("payto_uri",
- &payto_uri),
- GNUNET_PQ_result_spec_auto_from_type ("wire_target_h_payto",
- &h_payto),
- TALER_PQ_result_spec_denom_pub ("denom_pub",
- &denom_pub),
- GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
- &coin_pub),
- GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
- &merchant_pub),
- 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
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- ctx->status = 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; i<num_results; i++)
- {
- struct TALER_GlobalFeeSet fees;
- struct GNUNET_TIME_Relative purse_timeout;
- struct GNUNET_TIME_Relative history_expiration;
- uint32_t purse_account_limit;
- struct GNUNET_TIME_Timestamp start_date;
- struct GNUNET_TIME_Timestamp end_date;
- struct TALER_MasterSignatureP master_sig;
- 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
- };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- gctx->status = 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; i<num_results; i++)
- {
- struct TALER_EXCHANGEDB_Deposit deposit;
- struct GNUNET_TIME_Timestamp exchange_timestamp;
- struct TALER_DenominationPublicKey denom_pub;
- bool done;
- uint64_t rowid;
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- &deposit.amount_with_fee),
- GNUNET_PQ_result_spec_timestamp ("wallet_timestamp",
- &deposit.timestamp),
- GNUNET_PQ_result_spec_timestamp ("exchange_timestamp",
- &exchange_timestamp),
- GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
- &deposit.merchant_pub),
- TALER_PQ_result_spec_denom_pub ("denom_pub",
- &denom_pub),
- GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
- &deposit.coin.coin_pub),
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
- &deposit.coin.h_age_commitment),
- &deposit.coin.no_age_commitment),
- GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
- &deposit.csig),
- GNUNET_PQ_result_spec_timestamp ("refund_deadline",
- &deposit.refund_deadline),
- GNUNET_PQ_result_spec_timestamp ("wire_deadline",
- &deposit.wire_deadline),
- GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
- &deposit.h_contract_terms),
- GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
- &deposit.wire_salt),
- GNUNET_PQ_result_spec_string ("receiver_wire_account",
- &deposit.receiver_wire_account),
- GNUNET_PQ_result_spec_bool ("done",
- &done),
- GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
- &rowid),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_GenericReturnValue ret;
-
- memset (&deposit,
- 0,
- sizeof (deposit));
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- dsc->status = 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; i<num_results; i++)
- {
- uint64_t rowid;
- struct TALER_Amount history_fee;
- struct GNUNET_TIME_Timestamp ts;
- struct TALER_ReservePublicKeyP reserve_pub;
- struct TALER_ReserveSignatureP reserve_sig;
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
- &history_fee),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
- &reserve_pub),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
- &reserve_sig),
- GNUNET_PQ_result_spec_uint64 ("history_request_serial_id",
- &rowid),
- GNUNET_PQ_result_spec_timestamp ("request_timestamp",
- &ts),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_GenericReturnValue ret;
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- dsc->status = 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; i<num_results; i++)
- {
- struct TALER_PurseContractPublicKeyP purse_pub;
- struct TALER_ReservePublicKeyP reserve_pub;
- bool no_reserve = true;
- uint64_t rowid;
- struct TALER_Amount val;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
- &purse_pub),
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
- &reserve_pub),
- &no_reserve),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- &val),
- GNUNET_PQ_result_spec_uint64 ("purse_deposit_serial_id",
- &rowid),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_GenericReturnValue ret;
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- dsc->status = 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; i<num_results; i++)
- {
- struct TALER_Amount amount_with_fee;
- struct TALER_CoinSpendPublicKeyP coin_pub;
- struct TALER_DenominationPublicKey denom_pub;
- uint64_t rowid;
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_denom_pub ("denom_pub",
- &denom_pub),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- &amount_with_fee),
- GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
- &coin_pub),
- GNUNET_PQ_result_spec_uint64 ("purse_deposit_serial_id",
- &rowid),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_GenericReturnValue ret;
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- dsc->status = 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; i<num_results; i++)
- {
- struct TALER_DenominationPublicKey denom_pub;
- struct TALER_CoinSpendPublicKeyP coin_pub;
- struct TALER_CoinSpendSignatureP coin_sig;
- struct TALER_AgeCommitmentHash h_age_commitment;
- bool ac_isnull;
- struct TALER_Amount amount_with_fee;
- uint32_t noreveal_index;
- uint64_t rowid;
- struct TALER_RefreshCommitmentP rc;
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_denom_pub ("denom_pub",
- &denom_pub),
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
- &h_age_commitment),
- &ac_isnull),
- GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
- &coin_pub),
- GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
- &coin_sig),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- &amount_with_fee),
- GNUNET_PQ_result_spec_uint32 ("noreveal_index",
- &noreveal_index),
- GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
- &rowid),
- GNUNET_PQ_result_spec_auto_from_type ("rc",
- &rc),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_GenericReturnValue ret;
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- rsc->status = 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; i<num_results; i++)
- {
- struct TALER_EXCHANGEDB_Refund refund;
- struct TALER_DenominationPublicKey denom_pub;
- uint64_t rowid;
- bool full_refund;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
- &refund.details.merchant_pub),
- GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
- &refund.details.merchant_sig),
- GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
- &refund.details.h_contract_terms),
- GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
- &refund.details.rtransaction_id),
- TALER_PQ_result_spec_denom_pub ("denom_pub",
- &denom_pub),
- GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
- &refund.coin.coin_pub),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- &refund.details.refund_amount),
- GNUNET_PQ_result_spec_uint64 ("refund_serial_id",
- &rowid),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_GenericReturnValue ret;
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- rsc->status = 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; i<num_results; i++)
- {
- struct TALER_ReservePublicKeyP reserve_pub;
- struct TALER_Amount credit;
- char *sender_account_details;
- struct GNUNET_TIME_Timestamp execution_date;
- uint64_t rowid;
- uint64_t wire_reference;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
- &reserve_pub),
- GNUNET_PQ_result_spec_uint64 ("wire_reference",
- &wire_reference),
- TALER_PQ_RESULT_SPEC_AMOUNT ("credit",
- &credit),
- GNUNET_PQ_result_spec_timestamp ("execution_date",
- &execution_date),
- GNUNET_PQ_result_spec_string ("sender_account_details",
- &sender_account_details),
- GNUNET_PQ_result_spec_uint64 ("reserve_in_serial_id",
- &rowid),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_GenericReturnValue ret;
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- risc->status = 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; i<num_results; i++)
- {
- struct TALER_BlindedCoinHashP h_blind_ev;
- struct TALER_DenominationPublicKey denom_pub;
- struct TALER_ReservePublicKeyP reserve_pub;
- struct TALER_ReserveSignatureP reserve_sig;
- struct GNUNET_TIME_Timestamp execution_date;
- struct TALER_Amount amount_with_fee;
- uint64_t rowid;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
- &h_blind_ev),
- TALER_PQ_result_spec_denom_pub ("denom_pub",
- &denom_pub),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
- &reserve_pub),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
- &reserve_sig),
- GNUNET_PQ_result_spec_timestamp ("execution_date",
- &execution_date),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- &amount_with_fee),
- GNUNET_PQ_result_spec_uint64 ("reserve_out_serial_id",
- &rowid),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_GenericReturnValue ret;
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- rosc->status = 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; i<num_results; i++)
- {
- uint64_t rowid;
- struct GNUNET_TIME_Timestamp date;
- struct TALER_WireTransferIdentifierRawP wtid;
- char *payto_uri;
- struct TALER_Amount amount;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("wireout_uuid",
- &rowid),
- GNUNET_PQ_result_spec_timestamp ("execution_date",
- &date),
- GNUNET_PQ_result_spec_auto_from_type ("wtid_raw",
- &wtid),
- GNUNET_PQ_result_spec_string ("payto_uri",
- &payto_uri),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
- &amount),
- GNUNET_PQ_result_spec_end
- };
- int ret;
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- wosc->status = 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; i<num_results; i++)
- {
- uint64_t rowid;
- struct TALER_ReservePublicKeyP reserve_pub;
- struct TALER_CoinPublicInfo coin;
- struct TALER_CoinSpendSignatureP coin_sig;
- union TALER_DenominationBlindingKeyP coin_blind;
- struct TALER_Amount amount;
- struct TALER_DenominationPublicKey denom_pub;
- struct TALER_BlindedCoinHashP h_blind_ev;
- struct GNUNET_TIME_Timestamp timestamp;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("recoup_uuid",
- &rowid),
- GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
- &timestamp),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
- &reserve_pub),
- GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
- &coin.coin_pub),
- TALER_PQ_result_spec_denom_pub ("denom_pub",
- &denom_pub),
- GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
- &coin_sig),
- GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
- &coin_blind),
- GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
- &h_blind_ev),
- GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
- &coin.denom_pub_hash),
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
- &coin.h_age_commitment),
- &coin.no_age_commitment),
- TALER_PQ_result_spec_denom_sig ("denom_sig",
- &coin.denom_sig),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
- &amount),
- GNUNET_PQ_result_spec_end
- };
- int ret;
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- psc->status = 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; i<num_results; i++)
- {
- uint64_t rowid;
- struct TALER_CoinSpendPublicKeyP old_coin_pub;
- struct TALER_CoinPublicInfo coin;
- struct TALER_CoinSpendSignatureP coin_sig;
- union TALER_DenominationBlindingKeyP coin_blind;
- struct TALER_DenominationPublicKey denom_pub;
- struct TALER_DenominationHashP old_denom_pub_hash;
- struct TALER_Amount amount;
- struct TALER_BlindedCoinHashP h_blind_ev;
- struct GNUNET_TIME_Timestamp timestamp;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("recoup_refresh_uuid",
- &rowid),
- GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
- &timestamp),
- GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
- &old_coin_pub),
- GNUNET_PQ_result_spec_auto_from_type ("old_denom_pub_hash",
- &old_denom_pub_hash),
- GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
- &coin.coin_pub),
- GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
- &coin_sig),
- GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
- &coin_blind),
- TALER_PQ_result_spec_denom_pub ("denom_pub",
- &denom_pub),
- GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
- &h_blind_ev),
- GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
- &coin.denom_pub_hash),
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
- &coin.h_age_commitment),
- &coin.no_age_commitment),
- TALER_PQ_result_spec_denom_sig ("denom_sig",
- &coin.denom_sig),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
- &amount),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_GenericReturnValue ret;
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- psc->status = 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.
*
* @param cls a configuration instance
@@ -5376,6 +899,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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchangedb/test_exchangedb_by_j.c
+ * @brief test cases for DB interaction functions
+ * @author Joseph Xu
+ */
+#include "platform.h"
+#include "taler_exchangedb_lib.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+/**o
+ * Global result from the testcase.
+ */
+static int result;
+
+/**
+ * Report line of error if @a cond is true, and jump to label "drop".
+ */
+#define FAILIF(cond) \
+ do { \
+ if (! (cond)) {break;} \
+ GNUNET_break (0); \
+ goto drop; \
+ } while (0)
+
+
+/**
+ * Initializes @a ptr with random data.
+ */
+#define RND_BLK(ptr) \
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr))
+
+/**
+ * Initializes @a ptr with zeros.
+ */
+#define ZR_BLK(ptr) \
+ memset (ptr, 0, sizeof (*ptr))
+
+
+/**
+ * Currency we use. Must match test-exchange-db-*.conf.
+ */
+#define CURRENCY "EUR"
+
+/**
+ * Database plugin under test.
+ */
+static struct TALER_EXCHANGEDB_Plugin *plugin;
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure with config
+ */
+static void
+run (void *cls)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ const uint32_t num_partitions = 10;
+
+ if (NULL ==
+ (plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
+ {
+ GNUNET_break (0);
+ result = 77;
+ return;
+ }
+ (void) plugin->drop_tables (plugin->cls);
+ if (GNUNET_OK !=
+ plugin->create_tables (plugin->cls))
+ {
+ 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; k<batch_size; k++)
+ {
+ RND_BLK (&reserves[k].reserve_pub);
+ reserves[k].balance = value;
+ reserves[k].execution_time = ts;
+ reserves[k].sender_account_details = sndr;
+ reserves[k].exchange_account_name = "name";
+ reserves[k].wire_reference = k;
+ }
+ FAILIF (batch_size !=
+ plugin->batch_reserves_in_insert (plugin->cls,
+ reserves,
+ batch_size,
+ results));
+ }
+
+ duration = GNUNET_TIME_absolute_get_duration (now);
+ fprintf (stdout,
+ "for a batchsize equal to %d it took %s\n",
+ batch_size,
+ GNUNET_STRINGS_relative_time_to_string (duration,
+ GNUNET_NO) );
+
+ }
+ result = 0;
+drop:
+ GNUNET_break (GNUNET_OK ==
+ plugin->drop_tables (plugin->cls));
+cleanup:
+ TALER_EXCHANGEDB_plugin_unload (plugin);
+ plugin = NULL;
+}
+
+
+int
+main (int argc,
+ char *const argv[])
+{
+ const char *plugin_name;
+ char *config_filename;
+ char *testname;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ (void) argc;
+ result = -1;
+ if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
+ {
+ GNUNET_break (0);
+ return -1;
+ }
+
+ GNUNET_log_setup (argv[0],
+ "WARNING",
+ NULL);
+ plugin_name++;
+ (void) GNUNET_asprintf (&testname,
+ "test-exchange-db-%s",
+ plugin_name);
+ (void) GNUNET_asprintf (&config_filename,
+ "%s.conf",
+ testname);
+ fprintf (stdout,
+ "Using config: %s\n",
+ config_filename);
+ cfg = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_parse (cfg,
+ config_filename))
+ {
+ GNUNET_break (0);
+ GNUNET_free (config_filename);
+ GNUNET_free (testname);
+ return 2;
+ }
+ GNUNET_SCHEDULER_run (&run,
+ cfg);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_free (config_filename);
+ GNUNET_free (testname);
+ return result;
+}
+
+/* end of test_exchangedb_by_j.c */
diff --git a/src/exchangedb/test_exchangedb_by_j.c b/src/exchangedb/test_exchangedb_by_j.c
index 0f252a097..834373b55 100644
--- a/src/exchangedb/test_exchangedb_by_j.c
+++ b/src/exchangedb/test_exchangedb_by_j.c
@@ -91,9 +91,9 @@ run (void *cls)
result = 77;
goto cleanup;
}
-<<<<<<< HEAD
+
for (unsigned int i = 0; i< 7; i++)
-=======
+
if (GNUNET_OK !=
plugin->setup_partitions (plugin->cls,
num_partitions))
@@ -104,7 +104,7 @@ run (void *cls)
}
for (unsigned int i = 0; i< 8; i++)
->>>>>>> 26922c6d (batch modifications)
+
{
static unsigned int batches[] = {1, 1,0, 2, 4, 16, 64, 256};
const char *sndr = "payto://x-taler-bank/localhost:8080/1";
@@ -123,15 +123,8 @@ run (void *cls)
ts = GNUNET_TIME_timestamp_get ();
for (unsigned int r = 0; r<10; r++)
{
-<<<<<<< HEAD
- plugin->start_read_committed (plugin->cls,
- "test_by_j");
-
-=======
- plugin->start (plugin->cls,
- "test_by_exchange_j");
->>>>>>> 26922c6d (batch modifications)
- for (unsigned int k = 0; k<batch_size; k++)
+
+ for (unsigned int k = 0; k<batch_size; k++)
{
RND_BLK (&reserves[k].reserve_pub);
reserves[k].balance = value;
@@ -139,44 +132,13 @@ run (void *cls)
reserves[k].sender_account_details = sndr;
reserves[k].exchange_account_name = "name";
reserves[k].wire_reference = k;
-<<<<<<< HEAD
-
- }
- FAILIF (batch_size !=
- plugin->batch_reserves_in_insert (plugin->cls,
- reserves,
- batch_size,
- results));
-
- plugin->commit (plugin->cls);
-=======
}
FAILIF (batch_size !=
plugin->batch_reserves_in_insert (plugin->cls,
reserves,
batch_size,
results));
- /*plugin->commit (plugin->cls);*/
->>>>>>> 26922c6d (batch modifications)
}
- /*
- for (unsigned int s=0;s<10;s++)
- {
- for (unsigned int k = 0; k<batch_size; k++)
- {
- RND_BLK (&reserves2[k].reserve_pub);
- reserves2[k].balance = value;
- reserves2[k].execution_time = ts;
- reserves2[k].sender_account_details = sndr;
- reserves2[k].exchange_account_name = "name";
- reserves2[k].wire_reference = k;
- }
- FAILIF (batch_size !=
- plugin->batch_reserves_in_insert (plugin->cls,
- reserves2,
- batch_size,
- results));
- }*/
duration = GNUNET_TIME_absolute_get_duration (now);
fprintf (stdout,