diff options
Diffstat (limited to 'src/exchangedb')
| -rw-r--r-- | src/exchangedb/.gitignore | 1 | ||||
| -rw-r--r-- | src/exchangedb/Makefile.am | 33 | ||||
| -rw-r--r-- | src/exchangedb/pg_batch2_reserves_in_insert.c | 282 | ||||
| -rw-r--r-- | src/exchangedb/pg_batch2_reserves_in_insert.h | 33 | ||||
| -rw-r--r-- | src/exchangedb/pg_batch4_reserves_in_insert.c | 266 | ||||
| -rw-r--r-- | src/exchangedb/pg_batch4_reserves_in_insert.h | 34 | ||||
| -rw-r--r-- | src/exchangedb/pg_batch_reserves_in_insert.c | 168 | ||||
| -rw-r--r-- | src/exchangedb/pg_get_link_data.c | 68 | ||||
| -rw-r--r-- | src/exchangedb/pg_get_policy_details.c | 2 | ||||
| -rw-r--r-- | src/exchangedb/pg_select_refunds_by_coin.c | 20 | ||||
| -rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 4558 | ||||
| -rw-r--r-- | src/exchangedb/test_exchangedb_batch_reserves_in_insert.c | 201 | ||||
| -rw-r--r-- | src/exchangedb/test_exchangedb_by_j.c | 35 | 
13 files changed, 1071 insertions, 4630 deletions
| diff --git a/src/exchangedb/.gitignore b/src/exchangedb/.gitignore index fcac98bc..abe50552 100644 --- a/src/exchangedb/.gitignore +++ b/src/exchangedb/.gitignore @@ -9,3 +9,4 @@ shard-drop0001.sqltest-exchangedb-by-j-postgres  test-exchangedb-by-j-postgres  perf-exchangedb-reserves-in-insert-postgres  exchange-0002.sql +procedures.sql diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am index 4d9bfcb5..a6eb6747 100644 --- a/src/exchangedb/Makefile.am +++ b/src/exchangedb/Makefile.am @@ -243,6 +243,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \    pg_select_account_merges_above_serial_id.h pg_select_account_merges_above_serial_id.c \    pg_select_all_purse_decisions_above_serial_id.h pg_select_all_purse_decisions_above_serial_id.c \    pg_batch_reserves_in_insert.h pg_batch_reserves_in_insert.c \ +  pg_batch2_reserves_in_insert.h pg_batch2_reserves_in_insert.c \    pg_select_reserve_open_above_serial_id.c pg_select_reserve_open_above_serial_id.h  libtaler_plugin_exchangedb_postgres_la_LIBADD = \    $(LTLIBINTL) @@ -280,12 +281,16 @@ check_PROGRAMS = \    test-exchangedb-postgres \    bench-db-postgres\    perf-exchangedb-reserves-in-insert-postgres\ -  test-exchangedb-by-j-postgres +  test-exchangedb-by-j-postgres\ +  test-exchangedb-batch-reserves-in-insert-postgres  AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;  TESTS = \    test-exchangedb-postgres\    test-exchangedb-by-j-postgres\ -  perf-exchangedb-reserves-in-insert-postgres +  perf-exchangedb-reserves-in-insert-postgres\ +  test-exchangedb-batch-reserves-in-insert-postgres + +  test_exchangedb_postgres_SOURCES = \    test_exchangedb.c  test_exchangedb_postgres_LDADD = \ @@ -333,5 +338,29 @@ bench_db_postgres_LDADD = \    -lgnunetutil \    $(XLIB) + +test_exchangedb_batch_reserves_in_insert_postgres_SOURCES = \ +  test_exchangedb_batch_reserves_in_insert.c +test_exchangedb_batch_reserves_in_insert_postgres_LDADD = \ +  libtalerexchangedb.la \ +  $(top_builddir)/src/json/libtalerjson.la \ +  $(top_builddir)/src/util/libtalerutil.la \ +  $(top_builddir)/src/pq/libtalerpq.la \ +  -ljansson \ +  -lgnunetjson \ +  -lgnunetutil \ +  $(XLIB) + +bench_db_postgres_SOURCES = \ +  bench_db.c +bench_db_postgres_LDADD = \ +  libtalerexchangedb.la \ +  $(top_builddir)/src/util/libtalerutil.la \ +  $(top_builddir)/src/pq/libtalerpq.la \ +  -lgnunetpq \ +  -lgnunetutil \ +  $(XLIB) + +  EXTRA_test_exchangedb_postgres_DEPENDENCIES = \    libtaler_plugin_exchangedb_postgres.la diff --git a/src/exchangedb/pg_batch2_reserves_in_insert.c b/src/exchangedb/pg_batch2_reserves_in_insert.c new file mode 100644 index 00000000..8aca11de --- /dev/null +++ b/src/exchangedb/pg_batch2_reserves_in_insert.c @@ -0,0 +1,282 @@ +/* +   This file is part of TALER +   Copyright (C) 2022 Taler Systems SA + +   TALER is free software; you can redistribute it and/or modify it under the +   terms of the GNU General Public License as published by the Free Software +   Foundation; either version 3, or (at your option) any later version. + +   TALER is distributed in the hope that it will be useful, but WITHOUT ANY +   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +   A PARTICULAR PURPOSE.  See the GNU General Public License for more details. + +   You should have received a copy of the GNU General Public License along with +   TALER; see the file COPYING.  If not, see <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; +  uint64_t reserve_uuid2; +  bool conflicted; +  bool conflicted2; +  bool transaction_duplicate; +  bool transaction_duplicate2; +  bool need_update = false; +  bool need_update2 = false; +  struct GNUNET_TIME_Timestamp reserve_expiration +    = GNUNET_TIME_relative_to_timestamp (pg->idle_reserve_expiration_time); +  bool conflicts[reserves_length]; +  bool conflicts2[reserves_length]; +  char *notify_s[reserves_length]; + +  if (GNUNET_OK != +      TEH_PG_preflight (pg)) +  { +    GNUNET_break (0); +    return GNUNET_DB_STATUS_HARD_ERROR; +  } +  PREPARE (pg, +           "reserve_create", +           "SELECT " +           "out_reserve_found AS conflicted" +           ",out_reserve_found2 AS conflicted2" +           ",transaction_duplicate" +           ",transaction_duplicate2" +           ",ruuid AS reserve_uuid" +           ",ruuid2 AS reserve_uuid2" +           " FROM batch2_reserves_insert" +           " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21);"); +  expiry = GNUNET_TIME_absolute_to_timestamp ( +    GNUNET_TIME_absolute_add (reserves->execution_time.abs_time, +                              pg->idle_reserve_expiration_time)); +  gc = GNUNET_TIME_absolute_to_timestamp ( +    GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), +                              pg->legal_reserve_expiration_time)); +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, +              "Creating reserve %s with expiration in %s\n", +              TALER_B2S (&(reserves->reserve_pub)), +              GNUNET_STRINGS_relative_time_to_string ( +                pg->idle_reserve_expiration_time, +                GNUNET_NO)); + +  { +    if (GNUNET_OK != +        TEH_PG_start_read_committed(pg, +                                   "READ_COMMITED")) +    { +      GNUNET_break (0); +      return GNUNET_DB_STATUS_HARD_ERROR; +    } +  } +  /* Optimistically assume this is a new reserve, create balance for the first +     time; we do this before adding the actual transaction to "reserves_in", +     as for a new reserve it can't be a duplicate 'add' operation, and as +     the 'add' operation needs the reserve entry as a foreign key. */ +  for (unsigned int i=0;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 & ~1);i+=2) +  { +    const struct TALER_EXCHANGEDB_ReserveInInfo *reserve0 = &reserves[i]; +    const struct TALER_EXCHANGEDB_ReserveInInfo *reserve1 = &reserves[i+1]; +    struct GNUNET_PQ_QueryParam params[] = { +      GNUNET_PQ_query_param_auto_from_type (&reserve0->reserve_pub), +      GNUNET_PQ_query_param_timestamp (&expiry), +      GNUNET_PQ_query_param_timestamp (&gc), +      GNUNET_PQ_query_param_uint64 (&reserve0->wire_reference), +      TALER_PQ_query_param_amount (&reserve0->balance), +      GNUNET_PQ_query_param_string (reserve0->exchange_account_name), +      GNUNET_PQ_query_param_timestamp (&reserve0->execution_time), +      GNUNET_PQ_query_param_auto_from_type (&h_payto), +      GNUNET_PQ_query_param_string (reserve0->sender_account_details), +      GNUNET_PQ_query_param_timestamp (&reserve_expiration), +      GNUNET_PQ_query_param_string (notify_s[i]), +      GNUNET_PQ_query_param_auto_from_type (&reserve1->reserve_pub), +      GNUNET_PQ_query_param_uint64 (&reserve1->wire_reference), +      TALER_PQ_query_param_amount (&reserve1->balance), +      GNUNET_PQ_query_param_string (reserve1->exchange_account_name), +      GNUNET_PQ_query_param_timestamp (&reserve1->execution_time), +      GNUNET_PQ_query_param_auto_from_type (&h_payto), +      GNUNET_PQ_query_param_string (reserve1->sender_account_details), +      GNUNET_PQ_query_param_timestamp (&reserve_expiration), + +      GNUNET_PQ_query_param_end +    }; + +    struct GNUNET_PQ_ResultSpec rs[] = { +      GNUNET_PQ_result_spec_bool ("conflicted", +                                  &conflicted), +      GNUNET_PQ_result_spec_bool ("conflicted2", +                                  &conflicted2), +      GNUNET_PQ_result_spec_bool ("transaction_duplicate", +                                  &transaction_duplicate), +      GNUNET_PQ_result_spec_bool ("transaction_duplicate2", +                                  &transaction_duplicate2), +      GNUNET_PQ_result_spec_uint64 ("reserve_uuid", +                                    &reserve_uuid), +      GNUNET_PQ_result_spec_uint64 ("reserve_uuid2", +                                    &reserve_uuid2), +      GNUNET_PQ_result_spec_end +    }; + +    TALER_payto_hash (reserve0->sender_account_details, +                      &h_payto); +    TALER_payto_hash (reserve1->sender_account_details, +                      &h_payto); + +    /* Note: query uses 'on conflict do nothing' */ +    qs1 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, +                                                    "reserve_create", +                                                    params, +                                                    rs); + +    if (qs1 < 0) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                  "Failed to create reserves (%d)\n", +                  qs1); +      return qs1; +    } +    if (reserves_length & 1) +    { +      const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[reserves_length-1]; +      // single insert logic here +    } +   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1); +   results[i] = (transaction_duplicate) +      ? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS +      : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; +   conflicts[i] = conflicted; +   conflicts2[i] = conflicted2; +   //    fprintf(stdout, "%d", conflicts[i]); +   // fprintf(stdout, "%d", conflicts2[i]); +   if ((!conflicts[i] && transaction_duplicate) ||(!conflicts2[i] && transaction_duplicate2)) +   { +     GNUNET_break (0); +     TEH_PG_rollback (pg); +     return GNUNET_DB_STATUS_HARD_ERROR; +   } +   need_update |= conflicted |= conflicted2; +  } +  // commit +  { +    enum GNUNET_DB_QueryStatus cs; + +    cs = TEH_PG_commit (pg); +    if (cs < 0) +      return cs; +  } + +  if (!need_update) +    goto exit; +  // begin serializable +  { +    if (GNUNET_OK != +        TEH_PG_start(pg, +                     "reserve-insert-continued")) +    { +      GNUNET_break (0); +      return GNUNET_DB_STATUS_HARD_ERROR; +    } +  } + +  enum GNUNET_DB_QueryStatus qs2; +  PREPARE (pg, +           "reserves_in_add_transaction", +           "SELECT batch_reserves_update" +           " ($1,$2,$3,$4,$5,$6,$7,$8,$9);"); +  for (unsigned int i=0;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 00000000..bb6be3f6 --- /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_batch4_reserves_in_insert.c b/src/exchangedb/pg_batch4_reserves_in_insert.c new file mode 100644 index 00000000..604a31e7 --- /dev/null +++ b/src/exchangedb/pg_batch4_reserves_in_insert.c @@ -0,0 +1,266 @@ +/* +   This file is part of TALER +   Copyright (C) 2022 Taler Systems SA + +   TALER is free software; you can redistribute it and/or modify it under the +   terms of the GNU General Public License as published by the Free Software +   Foundation; either version 3, or (at your option) any later version. + +   TALER is distributed in the hope that it will be useful, but WITHOUT ANY +   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +   A PARTICULAR PURPOSE.  See the GNU General Public License for more details. + +   You should have received a copy of the GNU General Public License along with +   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file exchangedb/pg_batch4_reserves_in_insert.c + * @brief Implementation of the reserves_in_insert function for Postgres + * @author Joseph Xu + */ +#include "platform.h" +#include "taler_error_codes.h" +#include "taler_dbevents.h" +#include "taler_pq_lib.h" +#include "pg_batch_reserves_in_insert.h" +#include "pg_helper.h" +#include "pg_start.h" +#include "pg_rollback.h" +#include "pg_start_read_committed.h" +#include "pg_commit.h" +#include "pg_reserves_get.h" +#include "pg_reserves_update.h" +#include "pg_setup_wire_target.h" +#include "pg_event_notify.h" +#include "pg_preflight.h" + +/** + * Generate event notification for the reserve change. + * + * @param reserve_pub reserve to notfiy on + */ +static char * +compute_notify_on_reserve (const struct TALER_ReservePublicKeyP *reserve_pub) +{ +  struct TALER_ReserveEventP rep = { +    .header.size = htons (sizeof (rep)), +    .header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING), +    .reserve_pub = *reserve_pub +  }; + +  return GNUNET_PG_get_event_notify_channel (&rep.header); +} + + +enum GNUNET_DB_QueryStatus +TEH_PG_batch4_reserves_in_insert (void *cls, +                                 const struct TALER_EXCHANGEDB_ReserveInInfo *reserves, +                                 unsigned int reserves_length, +                                 enum GNUNET_DB_QueryStatus *results) +{ +  struct PostgresClosure *pg = cls; +  enum GNUNET_DB_QueryStatus qs1; +  struct GNUNET_TIME_Timestamp expiry; +  struct GNUNET_TIME_Timestamp gc; +  struct TALER_PaytoHashP h_payto; +  uint64_t reserve_uuid; +  bool conflicted; +  bool conflicted2; +  bool transaction_duplicate; +  bool transaction_duplicate2; +  bool need_update = false; +  bool need_update2 = false; +  struct GNUNET_TIME_Timestamp reserve_expiration +    = GNUNET_TIME_relative_to_timestamp (pg->idle_reserve_expiration_time); +  bool conflicts[reserves_length]; +  bool conflicts2[reserves_length]; +  char *notify_s[reserves_length]; + +  if (GNUNET_OK != +      TEH_PG_preflight (pg)) +  { +    GNUNET_break (0); +    return GNUNET_DB_STATUS_HARD_ERROR; +  } +  PREPARE (pg, +           "reserve_create", +           "SELECT " +           "out_reserve_found AS conflicted" +           ",out_reserve_found2 AS conflicted2" +           ",transaction_duplicate" +           ",transaction_duplicate2" +           ",ruuid AS reserve_uuid" +           " FROM batch2_reserves_insert" +           " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21);"); +  expiry = GNUNET_TIME_absolute_to_timestamp ( +    GNUNET_TIME_absolute_add (reserves->execution_time.abs_time, +                              pg->idle_reserve_expiration_time)); +  gc = GNUNET_TIME_absolute_to_timestamp ( +    GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), +                              pg->legal_reserve_expiration_time)); +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, +              "Creating reserve %s with expiration in %s\n", +              TALER_B2S (&(reserves->reserve_pub)), +              GNUNET_STRINGS_relative_time_to_string ( +                pg->idle_reserve_expiration_time, +                GNUNET_NO)); + +  { +    if (GNUNET_OK != +        TEH_PG_start_read_committed(pg, +                                   "READ_COMMITED")) +    { +      GNUNET_break (0); +      return GNUNET_DB_STATUS_HARD_ERROR; +    } +  } +  /* Optimistically assume this is a new reserve, create balance for the first +     time; we do this before adding the actual transaction to "reserves_in", +     as for a new reserve it can't be a duplicate 'add' operation, and as +     the 'add' operation needs the reserve entry as a foreign key. */ +  for (unsigned int i=0;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), +      GNUNET_PQ_query_param_uint64 (&reserve->wire_reference), +      TALER_PQ_query_param_amount (&reserve->balance), +      GNUNET_PQ_query_param_string (reserve->exchange_account_name), +      GNUNET_PQ_query_param_timestamp (&reserve->execution_time), +      GNUNET_PQ_query_param_auto_from_type (&h_payto), +      GNUNET_PQ_query_param_string (reserve->sender_account_details), +      GNUNET_PQ_query_param_timestamp (&reserve_expiration), +      GNUNET_PQ_query_param_end +    }; + +    struct GNUNET_PQ_ResultSpec rs[] = { +      GNUNET_PQ_result_spec_bool ("conflicted", +                                  &conflicted), +            GNUNET_PQ_result_spec_bool ("conflicted2", +                                  &conflicted2), +      GNUNET_PQ_result_spec_bool ("transaction_duplicate", +                                  &transaction_duplicate), +      GNUNET_PQ_result_spec_uint64 ("reserve_uuid", +                                    &reserve_uuid), +      GNUNET_PQ_result_spec_end +    }; + +    TALER_payto_hash (reserve->sender_account_details, +                      &h_payto); + +    /* Note: query uses 'on conflict do nothing' */ +    qs1 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, +                                                    "reserve_create", +                                                    params, +                                                    rs); + +    if (qs1 < 0) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                  "Failed to create reserves (%d)\n", +                  qs1); +      return qs1; +    } +   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1); +   results[i] = (transaction_duplicate) +      ? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS +      : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; +   conflicts[i] = conflicted; +   conflicts2[i] = conflicted2; +   //  fprintf(stdout, "%d", conflicts[i]); +   if (!conflicts[i] && !conflicts2[i]&& transaction_duplicate) +   { +     GNUNET_break (0); +     TEH_PG_rollback (pg); +     return GNUNET_DB_STATUS_HARD_ERROR; +   } +   need_update |= conflicted |= conflicted2; +  } +  // commit +  { +    enum GNUNET_DB_QueryStatus cs; + +    cs = TEH_PG_commit (pg); +    if (cs < 0) +      return cs; +  } + +  if (!need_update) +    goto exit; +  // begin serializable +  { +    if (GNUNET_OK != +        TEH_PG_start(pg, +                     "reserve-insert-continued")) +    { +      GNUNET_break (0); +      return GNUNET_DB_STATUS_HARD_ERROR; +    } +  } + +  enum GNUNET_DB_QueryStatus qs2; +  PREPARE (pg, +           "reserves_in_add_transaction", +           "SELECT batch_reserves_update" +           " ($1,$2,$3,$4,$5,$6,$7,$8,$9);"); +  for (unsigned int i=0;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_batch4_reserves_in_insert.h b/src/exchangedb/pg_batch4_reserves_in_insert.h new file mode 100644 index 00000000..3b3a629f --- /dev/null +++ b/src/exchangedb/pg_batch4_reserves_in_insert.h @@ -0,0 +1,34 @@ +/* +   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_batch4_reserves_in_insert.h + * @brief implementation of the batch4_reserves_in_insert function for Postgres + * @author XU Joseph + */ +#ifndef PG_BATCH4_RESERVES_IN_INSERT_H +#define PG_BATCH4_RESERVES_IN_INSERT_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" + +enum GNUNET_DB_QueryStatus +TEH_PG_batch4_reserves_in_insert (void *cls, +                                 const struct TALER_EXCHANGEDB_ReserveInInfo *reserves, +                                 unsigned int reserves_length, +                                  enum GNUNET_DB_QueryStatus *results); + +#endif diff --git a/src/exchangedb/pg_batch_reserves_in_insert.c b/src/exchangedb/pg_batch_reserves_in_insert.c index 455f080d..8a14022e 100644 --- a/src/exchangedb/pg_batch_reserves_in_insert.c +++ b/src/exchangedb/pg_batch_reserves_in_insert.c @@ -16,7 +16,7 @@  /**   * @file exchangedb/pg_batch_reserves_in_insert.c   * @brief Implementation of the reserves_in_insert function for Postgres - * @author JOSEPHxu + * @author Joseph Xu   */  #include "platform.h"  #include "taler_error_codes.h" @@ -32,18 +32,16 @@  #include "pg_reserves_update.h"  #include "pg_setup_wire_target.h"  #include "pg_event_notify.h" +#include "pg_preflight.h"  /** - * Generate event notification for the reserve - * change. + * Generate event notification for the reserve change.   * - * @param pg plugin state   * @param reserve_pub reserve to notfiy on   */ -static void -notify_on_reserve (struct PostgresClosure *pg, -                   const struct TALER_ReservePublicKeyP *reserve_pub) +static char * +compute_notify_on_reserve (const struct TALER_ReservePublicKeyP *reserve_pub)  {    struct TALER_ReserveEventP rep = {      .header.size = htons (sizeof (rep)), @@ -51,12 +49,7 @@ notify_on_reserve (struct PostgresClosure *pg,      .reserve_pub = *reserve_pub    }; -  GNUNET_log (GNUNET_ERROR_TYPE_INFO, -              "Notifying on reserve!\n"); -  TEH_PG_event_notify (pg, -                       &rep.header, -                       NULL, -                       0); +  return GNUNET_PG_get_event_notify_channel (&rep.header);  } @@ -75,17 +68,26 @@ TEH_PG_batch_reserves_in_insert (void *cls,    uint64_t reserve_uuid;    bool conflicted;    bool transaction_duplicate; +  bool need_update = false;    struct GNUNET_TIME_Timestamp reserve_expiration      = GNUNET_TIME_relative_to_timestamp (pg->idle_reserve_expiration_time); +  bool conflicts[reserves_length]; +  char *notify_s[reserves_length]; +  if (GNUNET_OK != +      TEH_PG_preflight (pg)) +  { +    GNUNET_break (0); +    return GNUNET_DB_STATUS_HARD_ERROR; +  }    PREPARE (pg,             "reserve_create",             "SELECT "             "out_reserve_found AS conflicted"             ",transaction_duplicate"             ",ruuid AS reserve_uuid" -           " FROM exchange_do_batch_reserves_in" -           " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11);"); +           " FROM batch_reserves_insert" +           " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12);");    expiry = GNUNET_TIME_absolute_to_timestamp (      GNUNET_TIME_absolute_add (reserves->execution_time.abs_time,                                pg->idle_reserve_expiration_time)); @@ -98,6 +100,16 @@ TEH_PG_batch_reserves_in_insert (void *cls,                GNUNET_STRINGS_relative_time_to_string (                  pg->idle_reserve_expiration_time,                  GNUNET_NO)); + +  { +    if (GNUNET_OK != +        TEH_PG_start_read_committed(pg, +                                   "READ_COMMITED")) +    { +      GNUNET_break (0); +      return GNUNET_DB_STATUS_HARD_ERROR; +    } +  }    /* Optimistically assume this is a new reserve, create balance for the first       time; we do this before adding the actual transaction to "reserves_in",       as for a new reserve it can't be a duplicate 'add' operation, and as @@ -105,47 +117,135 @@ TEH_PG_batch_reserves_in_insert (void *cls,    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), /*$1*/ -      GNUNET_PQ_query_param_timestamp (&expiry),  /*$4*/ -      GNUNET_PQ_query_param_timestamp (&gc),  /*$5*/ -      GNUNET_PQ_query_param_uint64 (&reserve->wire_reference), /*6*/ -      TALER_PQ_query_param_amount (&reserve->balance), /*7+8*/ -      GNUNET_PQ_query_param_string (reserve->exchange_account_name), /*9*/ -      GNUNET_PQ_query_param_timestamp (&reserve->execution_time), /*10*/ -      GNUNET_PQ_query_param_auto_from_type (&h_payto), /*11*/ -      GNUNET_PQ_query_param_string (reserve->sender_account_details),/*12*/ -      GNUNET_PQ_query_param_timestamp (&reserve_expiration),/*13*/ +      GNUNET_PQ_query_param_auto_from_type (&reserve->reserve_pub), +      GNUNET_PQ_query_param_timestamp (&expiry), +      GNUNET_PQ_query_param_timestamp (&gc), +      GNUNET_PQ_query_param_uint64 (&reserve->wire_reference), +      TALER_PQ_query_param_amount (&reserve->balance), +      GNUNET_PQ_query_param_string (reserve->exchange_account_name), +      GNUNET_PQ_query_param_timestamp (&reserve->execution_time), +      GNUNET_PQ_query_param_auto_from_type (&h_payto), +      GNUNET_PQ_query_param_string (reserve->sender_account_details), +      GNUNET_PQ_query_param_timestamp (&reserve_expiration), +      GNUNET_PQ_query_param_string (notify_s[i]),        GNUNET_PQ_query_param_end      }; -    /* We should get all our results into results[]*/ +      struct GNUNET_PQ_ResultSpec rs[] = { -      GNUNET_PQ_result_spec_uint64 ("reserve_uuid", -                                    &reserve_uuid),        GNUNET_PQ_result_spec_bool ("conflicted",                                    &conflicted),        GNUNET_PQ_result_spec_bool ("transaction_duplicate",                                    &transaction_duplicate), +      GNUNET_PQ_result_spec_uint64 ("reserve_uuid", +                                    &reserve_uuid),        GNUNET_PQ_result_spec_end      };      TALER_payto_hash (reserve->sender_account_details,                        &h_payto); +      /* Note: query uses 'on conflict do nothing' */      qs1 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,                                                      "reserve_create",                                                      params,                                                      rs); +      if (qs1 < 0) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                  "Failed to create reserves (%d)\n", +                  qs1);        return qs1; -    notify_on_reserve (pg, -                       &reserve->reserve_pub); -    GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1); -    results[i] = (transaction_duplicate) +    } +   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1); +   results[i] = (transaction_duplicate)        ? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS        : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; -    if ( (! conflicted) && transaction_duplicate) -      TEH_PG_rollback (pg); +   conflicts[i] = conflicted; +   //  fprintf(stdout, "%d", conflicts[i]); +   if (!conflicts[i] && transaction_duplicate) +   { +     GNUNET_break (0); +     TEH_PG_rollback (pg); +     return GNUNET_DB_STATUS_HARD_ERROR; +   } +   need_update |= conflicted;    } +  // commit +  { +    enum GNUNET_DB_QueryStatus cs; + +    cs = TEH_PG_commit (pg); +    if (cs < 0) +      return cs; +  } + +  if (!need_update) +    goto exit; +  // begin serializable +  { +    if (GNUNET_OK != +        TEH_PG_start(pg, +                     "reserve-insert-continued")) +    { +      GNUNET_break (0); +      return GNUNET_DB_STATUS_HARD_ERROR; +    } +  } + +  enum GNUNET_DB_QueryStatus qs2; +  PREPARE (pg, +           "reserves_in_add_transaction", +           "SELECT batch_reserves_update" +           " ($1,$2,$3,$4,$5,$6,$7,$8,$9);"); +  for (unsigned int i=0;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_get_link_data.c b/src/exchangedb/pg_get_link_data.c index 93086289..f15bf35a 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_get_policy_details.c b/src/exchangedb/pg_get_policy_details.c index 5dacb600..fafdca53 100644 --- a/src/exchangedb/pg_get_policy_details.c +++ b/src/exchangedb/pg_get_policy_details.c @@ -57,7 +57,7 @@ TEH_PG_get_policy_details (    }; -   +    return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,                                                     "get_policy_details",                                                     params, diff --git a/src/exchangedb/pg_select_refunds_by_coin.c b/src/exchangedb/pg_select_refunds_by_coin.c index 6510ae4a..84b63a71 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 d04df467..c805810b 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -60,7 +60,7 @@  #include <pthread.h>  #include <libpq-fe.h> -/**WHAT I ADD**/ +/**NEW INCLUDES**/  #include "pg_insert_purse_request.h"  #include "pg_iterate_active_signkeys.h"  #include "pg_preflight.h" @@ -205,6 +205,7 @@  #include "pg_setup_wire_target.h"  #include "pg_compute_shard.h"  #include "pg_batch_reserves_in_insert.h" +#include "pg_batch2_reserves_in_insert.h"  /**   * Set to 1 to enable Postgres auto_explain module. This will   * slow down things a _lot_, but also provide extensive logging @@ -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", -                                       ×tamp), -      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", -                                       ×tamp), -      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 @@ -4960,81 +483,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)    }    plugin = GNUNET_new (struct TALER_EXCHANGEDB_Plugin);    plugin->cls = pg; -  plugin->get_policy_details = &postgres_get_policy_details; -  plugin->add_policy_fulfillment_proof = &postgres_add_policy_fulfillment_proof; -  plugin->do_melt = &postgres_do_melt; -  plugin->do_refund = &postgres_do_refund; -  plugin->do_recoup = &postgres_do_recoup; -  plugin->do_recoup_refresh = &postgres_do_recoup_refresh; -  plugin->get_reserve_balance = &postgres_get_reserve_balance; -  plugin->count_known_coins = &postgres_count_known_coins; -  plugin->ensure_coin_known = &postgres_ensure_coin_known; -  plugin->get_known_coin = &postgres_get_known_coin; -  plugin->get_coin_denomination = &postgres_get_coin_denomination; -  plugin->have_deposit2 = &postgres_have_deposit2; -  plugin->aggregate = &postgres_aggregate; -  plugin->create_aggregation_transient -    = &postgres_create_aggregation_transient; -  plugin->select_aggregation_transient -    = &postgres_select_aggregation_transient; -  plugin->find_aggregation_transient -    = &postgres_find_aggregation_transient; -  plugin->update_aggregation_transient -    = &postgres_update_aggregation_transient; -  plugin->get_ready_deposit = &postgres_get_ready_deposit; -  plugin->insert_deposit = &postgres_insert_deposit; -  plugin->insert_refund = &postgres_insert_refund; -  plugin->select_refunds_by_coin = &postgres_select_refunds_by_coin; -  plugin->get_melt = &postgres_get_melt; -  plugin->insert_refresh_reveal = &postgres_insert_refresh_reveal; -  plugin->get_refresh_reveal = &postgres_get_refresh_reveal; -  plugin->lookup_wire_transfer = &postgres_lookup_wire_transfer; -  plugin->lookup_transfer_by_deposit = &postgres_lookup_transfer_by_deposit; -  plugin->insert_wire_fee = &postgres_insert_wire_fee; -  plugin->insert_global_fee = &postgres_insert_global_fee; -  plugin->get_wire_fee = &postgres_get_wire_fee; -  plugin->get_global_fee = &postgres_get_global_fee; -  plugin->get_global_fees = &postgres_get_global_fees; -  plugin->insert_reserve_closed = &postgres_insert_reserve_closed; -  plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert; -  plugin->wire_prepare_data_mark_finished = -    &postgres_wire_prepare_data_mark_finished; -  plugin->wire_prepare_data_mark_failed = -    &postgres_wire_prepare_data_mark_failed; -  plugin->wire_prepare_data_get = &postgres_wire_prepare_data_get; -  plugin->start_deferred_wire_out = &postgres_start_deferred_wire_out; -  plugin->store_wire_transfer_out = &postgres_store_wire_transfer_out; -  plugin->gc = &postgres_gc; - -  plugin->select_deposits_above_serial_id -    = &postgres_select_deposits_above_serial_id; -  plugin->select_history_requests_above_serial_id -    = &postgres_select_history_requests_above_serial_id; -  plugin->select_purse_decisions_above_serial_id -    = &postgres_select_purse_decisions_above_serial_id; -  plugin->select_purse_deposits_by_purse -    = &postgres_select_purse_deposits_by_purse; -  plugin->select_refreshes_above_serial_id -    = &postgres_select_refreshes_above_serial_id; -  plugin->select_refunds_above_serial_id -    = &postgres_select_refunds_above_serial_id; -  plugin->select_reserves_in_above_serial_id -    = &postgres_select_reserves_in_above_serial_id; -  plugin->select_reserves_in_above_serial_id_by_account -    = &postgres_select_reserves_in_above_serial_id_by_account; -  plugin->select_withdrawals_above_serial_id -    = &postgres_select_withdrawals_above_serial_id; -  plugin->select_wire_out_above_serial_id -    = &postgres_select_wire_out_above_serial_id; -  plugin->select_wire_out_above_serial_id_by_account -    = &postgres_select_wire_out_above_serial_id_by_account; -  plugin->select_recoup_above_serial_id -    = &postgres_select_recoup_above_serial_id; -  plugin->select_recoup_refresh_above_serial_id -    = &postgres_select_recoup_refresh_above_serial_id; -  plugin->get_reserve_by_h_blind -    = &postgres_get_reserve_by_h_blind; -    /* New style, sort alphabetically! */    plugin->do_reserve_open      = &TEH_PG_do_reserve_open; @@ -5376,6 +824,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)      = &TEH_PG_set_purse_balance;    plugin->batch_reserves_in_insert      = &TEH_PG_batch_reserves_in_insert; +  plugin->batch2_reserves_in_insert +    = &TEH_PG_batch2_reserves_in_insert;    return plugin;  } diff --git a/src/exchangedb/test_exchangedb_batch_reserves_in_insert.c b/src/exchangedb/test_exchangedb_batch_reserves_in_insert.c new file mode 100644 index 00000000..460778b8 --- /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 43f47167..834373b5 100644 --- a/src/exchangedb/test_exchangedb_by_j.c +++ b/src/exchangedb/test_exchangedb_by_j.c @@ -91,9 +91,22 @@ run (void *cls)      result = 77;      goto cleanup;    } +    for (unsigned int i = 0; i< 7; i++) + +  if (GNUNET_OK != +      plugin->setup_partitions (plugin->cls, +                                num_partitions)) +  { +    GNUNET_break (0); +    result = 77; +    goto cleanup; +  } + +  for (unsigned int i = 0; i< 8; i++) +    { -    static unsigned int batches[] = {1, 1, 2, 4, 16, 64, 256}; +    static unsigned int batches[] = {1, 1,0, 2, 4, 16, 64, 256};      const char *sndr = "payto://x-taler-bank/localhost:8080/1";      struct TALER_Amount value;      unsigned int batch_size = batches[i]; @@ -101,6 +114,7 @@ run (void *cls)      struct GNUNET_TIME_Timestamp ts;      struct GNUNET_TIME_Relative duration;      struct TALER_EXCHANGEDB_ReserveInInfo reserves[batch_size]; +    /*   struct TALER_EXCHANGEDB_ReserveInInfo reserves2[batch_size];*/      enum GNUNET_DB_QueryStatus results[batch_size];      GNUNET_assert (GNUNET_OK ==                     TALER_string_to_amount (CURRENCY ":1.000010", @@ -109,10 +123,8 @@ run (void *cls)      ts = GNUNET_TIME_timestamp_get ();      for (unsigned int r = 0; r<10; r++)      { -      plugin->start_read_committed (plugin->cls, -                                    "test_by_j"); -      for (unsigned int k = 0; k<batch_size; k++) +       for (unsigned int k = 0; k<batch_size; k++)        {          RND_BLK (&reserves[k].reserve_pub);          reserves[k].balance = value; @@ -120,22 +132,21 @@ run (void *cls)          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)); - -      plugin->commit (plugin->cls); +            plugin->batch_reserves_in_insert (plugin->cls, +                                              reserves, +                                              batch_size, +                                              results));      } +      duration = GNUNET_TIME_absolute_get_duration (now);      fprintf (stdout,               "for a batchsize equal to %d it took %s\n",               batch_size,               GNUNET_STRINGS_relative_time_to_string (duration,                                                       GNUNET_NO) ); +    }    result = 0;  drop: @@ -155,7 +166,6 @@ main (int argc,    char *config_filename;    char *testname;    struct GNUNET_CONFIGURATION_Handle *cfg; -    (void) argc;    result = -1;    if (NULL == (plugin_name = strrchr (argv[0], (int) '-'))) @@ -163,6 +173,7 @@ main (int argc,      GNUNET_break (0);      return -1;    } +    GNUNET_log_setup (argv[0],                      "WARNING",                      NULL); | 
