LCOV - code coverage report
Current view: top level - exchangedb - plugin_exchangedb_postgres.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 37 4334 0.9 %
Date: 2022-08-25 06:15:09 Functions: 2 215 0.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    This file is part of TALER
       3             :    Copyright (C) 2014--2022 Taler Systems SA
       4             : 
       5             :    TALER is free software; you can redistribute it and/or modify it under the
       6             :    terms of the GNU General Public License as published by the Free Software
       7             :    Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :    TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :    A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12             : 
      13             :    You should have received a copy of the GNU General Public License along with
      14             :    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             :  */
      16             : 
      17             : /**
      18             :  * @file plugin_exchangedb_postgres.c
      19             :  * @brief Low-level (statement-level) Postgres database access for the exchange
      20             :  * @author Florian Dold
      21             :  * @author Christian Grothoff
      22             :  * @author Sree Harsha Totakura
      23             :  * @author Marcello Stanisci
      24             :  */
      25             : #include "platform.h"
      26             : #include "taler_error_codes.h"
      27             : #include "taler_dbevents.h"
      28             : #include "taler_pq_lib.h"
      29             : #include "taler_util.h"
      30             : #include "taler_json_lib.h"
      31             : #include "taler_exchangedb_plugin.h"
      32             : #include <poll.h>
      33             : #include <pthread.h>
      34             : #include <libpq-fe.h>
      35             : 
      36             : #include "plugin_exchangedb_common.c"
      37             : 
      38             : /**
      39             :  * Set to 1 to enable Postgres auto_explain module. This will
      40             :  * slow down things a _lot_, but also provide extensive logging
      41             :  * in the Postgres database logger for performance analysis.
      42             :  */
      43             : #define AUTO_EXPLAIN 1
      44             : 
      45             : /**
      46             :  * Wrapper macro to add the currency from the plugin's state
      47             :  * when fetching amounts from the database.
      48             :  *
      49             :  * @param field name of the database field to fetch amount from
      50             :  * @param[out] amountp pointer to amount to set
      51             :  */
      52             : #define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) TALER_PQ_result_spec_amount ( \
      53             :     field,pg->currency,amountp)
      54             : 
      55             : /**
      56             :  * Wrapper macro to add the currency from the plugin's state
      57             :  * when fetching amounts from the database.  NBO variant.
      58             :  *
      59             :  * @param field name of the database field to fetch amount from
      60             :  * @param[out] amountp pointer to amount to set
      61             :  */
      62             : #define TALER_PQ_RESULT_SPEC_AMOUNT_NBO(field,                          \
      63             :                                         amountp) TALER_PQ_result_spec_amount_nbo ( \
      64             :     field,pg->currency,amountp)
      65             : 
      66             : /**
      67             :  * Log a really unexpected PQ error with all the details we can get hold of.
      68             :  *
      69             :  * @param result PQ result object of the PQ operation that failed
      70             :  * @param conn SQL connection that was used
      71             :  */
      72             : #define BREAK_DB_ERR(result,conn) do {                                  \
      73             :     GNUNET_break (0);                                                   \
      74             :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,                                \
      75             :                 "Database failure: %s/%s/%s/%s/%s",                     \
      76             :                 PQresultErrorField (result, PG_DIAG_MESSAGE_PRIMARY),   \
      77             :                 PQresultErrorField (result, PG_DIAG_MESSAGE_DETAIL),    \
      78             :                 PQresultErrorMessage (result),                          \
      79             :                 PQresStatus (PQresultStatus (result)),                  \
      80             :                 PQerrorMessage (conn));                                 \
      81             : } while (0)
      82             : 
      83             : 
      84             : /**
      85             :  * Type of the "cls" argument given to each of the functions in
      86             :  * our API.
      87             :  */
      88             : struct PostgresClosure
      89             : {
      90             : 
      91             :   /**
      92             :    * Our configuration.
      93             :    */
      94             :   const struct GNUNET_CONFIGURATION_Handle *cfg;
      95             : 
      96             :   /**
      97             :    * Directory with SQL statements to run to create tables.
      98             :    */
      99             :   char *sql_dir;
     100             : 
     101             :   /**
     102             :    * After how long should idle reserves be closed?
     103             :    */
     104             :   struct GNUNET_TIME_Relative idle_reserve_expiration_time;
     105             : 
     106             :   /**
     107             :    * After how long should reserves that have seen withdraw operations
     108             :    * be garbage collected?
     109             :    */
     110             :   struct GNUNET_TIME_Relative legal_reserve_expiration_time;
     111             : 
     112             :   /**
     113             :    * What delay should we introduce before ready transactions
     114             :    * are actually aggregated?
     115             :    */
     116             :   struct GNUNET_TIME_Relative aggregator_shift;
     117             : 
     118             :   /**
     119             :    * Which currency should we assume all amounts to be in?
     120             :    */
     121             :   char *currency;
     122             : 
     123             :   /**
     124             :    * Our base URL.
     125             :    */
     126             :   char *exchange_url;
     127             : 
     128             :   /**
     129             :    * Postgres connection handle.
     130             :    */
     131             :   struct GNUNET_PQ_Context *conn;
     132             : 
     133             :   /**
     134             :    * Name of the current transaction, for debugging.
     135             :    */
     136             :   const char *transaction_name;
     137             : 
     138             :   /**
     139             :    * Did we initialize the prepared statements
     140             :    * for this session?
     141             :    */
     142             :   bool init;
     143             : 
     144             : };
     145             : 
     146             : 
     147             : /**
     148             :  * Drop all Taler tables.  This should only be used by testcases.
     149             :  *
     150             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     151             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
     152             :  */
     153             : static enum GNUNET_GenericReturnValue
     154           0 : postgres_drop_tables (void *cls)
     155             : {
     156           0 :   struct PostgresClosure *pg = cls;
     157             :   struct GNUNET_PQ_Context *conn;
     158             :   enum GNUNET_GenericReturnValue ret;
     159             : 
     160           0 :   if (NULL != pg->conn)
     161             :   {
     162           0 :     GNUNET_PQ_disconnect (pg->conn);
     163           0 :     pg->conn = NULL;
     164           0 :     pg->init = false;
     165             :   }
     166           0 :   conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
     167             :                                      "exchangedb-postgres",
     168             :                                      NULL,
     169             :                                      NULL,
     170             :                                      NULL);
     171           0 :   if (NULL == conn)
     172           0 :     return GNUNET_SYSERR;
     173           0 :   ret = GNUNET_PQ_exec_sql (conn,
     174             :                             "drop");
     175           0 :   GNUNET_PQ_disconnect (conn);
     176           0 :   return ret;
     177             : }
     178             : 
     179             : 
     180             : /**
     181             :  * Create the necessary tables if they are not present
     182             :  *
     183             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     184             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
     185             :  */
     186             : static enum GNUNET_GenericReturnValue
     187           0 : postgres_create_tables (void *cls)
     188             : {
     189           0 :   struct PostgresClosure *pg = cls;
     190             :   struct GNUNET_PQ_Context *conn;
     191             :   enum GNUNET_GenericReturnValue ret;
     192             : 
     193           0 :   conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
     194             :                                      "exchangedb-postgres",
     195             :                                      "exchange-",
     196             :                                      NULL,
     197             :                                      NULL);
     198           0 :   if (NULL == conn)
     199           0 :     return GNUNET_SYSERR;
     200           0 :   ret = GNUNET_PQ_exec_sql (conn,
     201             :                             "procedures");
     202           0 :   GNUNET_PQ_disconnect (conn);
     203           0 :   return ret;
     204             : }
     205             : 
     206             : 
     207             : /**
     208             :  * Create tables of a shard node with index idx
     209             :  *
     210             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     211             :  * @param idx the shards index, will be appended as suffix to all tables
     212             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
     213             :  */
     214             : static enum GNUNET_GenericReturnValue
     215           0 : postgres_create_shard_tables (void *cls,
     216             :                               uint32_t idx)
     217             : {
     218           0 :   struct PostgresClosure *pg = cls;
     219             :   struct GNUNET_PQ_Context *conn;
     220           0 :   enum GNUNET_GenericReturnValue ret = GNUNET_OK;
     221           0 :   struct GNUNET_PQ_QueryParam params[] = {
     222           0 :     GNUNET_PQ_query_param_uint32 (&idx),
     223             :     GNUNET_PQ_query_param_end
     224             :   };
     225           0 :   struct GNUNET_PQ_ExecuteStatement es[] = {
     226           0 :     GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"),
     227             :     GNUNET_PQ_EXECUTE_STATEMENT_END
     228             :   };
     229             : 
     230           0 :   struct GNUNET_PQ_PreparedStatement ps[] = {
     231           0 :     GNUNET_PQ_make_prepare ("create_shard_tables",
     232             :                             "SELECT"
     233             :                             " setup_shard"
     234             :                             " ($1);",
     235             :                             1),
     236             :     GNUNET_PQ_PREPARED_STATEMENT_END
     237             :   };
     238             : 
     239           0 :   conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
     240             :                                      "exchangedb-postgres",
     241             :                                      "shard-",
     242             :                                      es,
     243             :                                      ps);
     244           0 :   if (NULL == conn)
     245           0 :     return GNUNET_SYSERR;
     246           0 :   if (0 > GNUNET_PQ_eval_prepared_non_select (conn,
     247             :                                               "create_shard_tables",
     248             :                                               params))
     249           0 :     ret = GNUNET_SYSERR;
     250           0 :   GNUNET_PQ_disconnect (conn);
     251           0 :   return ret;
     252             : }
     253             : 
     254             : 
     255             : /**
     256             :  * Setup partitions of already existing tables
     257             :  *
     258             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     259             :  * @param num the number of partitions to create for each partitioned table
     260             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
     261             :  */
     262             : static enum GNUNET_GenericReturnValue
     263           0 : postgres_setup_partitions (void *cls,
     264             :                            uint32_t num)
     265             : {
     266           0 :   struct PostgresClosure *pg = cls;
     267             :   struct GNUNET_PQ_Context *conn;
     268           0 :   enum GNUNET_GenericReturnValue ret = GNUNET_OK;
     269           0 :   struct GNUNET_PQ_QueryParam params[] = {
     270           0 :     GNUNET_PQ_query_param_uint32 (&num),
     271             :     GNUNET_PQ_query_param_end
     272             :   };
     273           0 :   struct GNUNET_PQ_PreparedStatement ps[] = {
     274           0 :     GNUNET_PQ_make_prepare ("setup_partitions",
     275             :                             "SELECT"
     276             :                             " create_partitions"
     277             :                             " ($1);",
     278             :                             1),
     279             :     GNUNET_PQ_PREPARED_STATEMENT_END
     280             :   };
     281           0 :   struct GNUNET_PQ_ExecuteStatement es[] = {
     282           0 :     GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"),
     283             :     GNUNET_PQ_EXECUTE_STATEMENT_END
     284             :   };
     285             : 
     286           0 :   conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
     287             :                                      "exchangedb-postgres",
     288             :                                      NULL,
     289             :                                      es,
     290             :                                      ps);
     291           0 :   if (NULL == conn)
     292           0 :     return GNUNET_SYSERR;
     293           0 :   ret = GNUNET_OK;
     294           0 :   if (0 > GNUNET_PQ_eval_prepared_non_select (conn,
     295             :                                               "setup_partitions",
     296             :                                               params))
     297           0 :     ret = GNUNET_SYSERR;
     298           0 :   GNUNET_PQ_disconnect (conn);
     299           0 :   return ret;
     300             : }
     301             : 
     302             : 
     303             : /**
     304             :  * Setup foreign servers (shards) for already existing tables
     305             :  *
     306             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     307             :  * @param num the number of foreign servers (shards) to create for each partitioned table
     308             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
     309             :  */
     310             : static enum GNUNET_GenericReturnValue
     311           0 : postgres_setup_foreign_servers (void *cls,
     312             :                                 uint32_t num)
     313             : {
     314           0 :   struct PostgresClosure *pg = cls;
     315             :   struct GNUNET_PQ_Context *conn;
     316           0 :   enum GNUNET_GenericReturnValue ret = GNUNET_OK;
     317           0 :   char *shard_domain = NULL;
     318           0 :   char *remote_user = NULL;
     319           0 :   char *remote_user_pw = NULL;
     320             : 
     321           0 :   if (GNUNET_OK !=
     322           0 :       GNUNET_CONFIGURATION_get_value_string (pg->cfg,
     323             :                                              "exchange",
     324             :                                              "SHARD_DOMAIN",
     325             :                                              &shard_domain))
     326             :   {
     327           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     328             :                                "exchange",
     329             :                                "SHARD_DOMAIN");
     330           0 :     return GNUNET_SYSERR;
     331             :   }
     332           0 :   if (GNUNET_OK !=
     333           0 :       GNUNET_CONFIGURATION_get_value_string (pg->cfg,
     334             :                                              "exchangedb-postgres",
     335             :                                              "SHARD_REMOTE_USER",
     336             :                                              &remote_user))
     337             :   {
     338           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     339             :                                "exchangedb-postgres",
     340             :                                "SHARD_REMOTE_USER");
     341           0 :     GNUNET_free (shard_domain);
     342           0 :     return GNUNET_SYSERR;
     343             :   }
     344           0 :   if (GNUNET_OK !=
     345           0 :       GNUNET_CONFIGURATION_get_value_string (pg->cfg,
     346             :                                              "exchangedb-postgres",
     347             :                                              "SHARD_REMOTE_USER_PW",
     348             :                                              &remote_user_pw))
     349             :   {
     350           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     351             :                                "exchangedb-postgres",
     352             :                                "SHARD_REMOTE_USER_PW");
     353           0 :     GNUNET_free (shard_domain);
     354           0 :     GNUNET_free (remote_user);
     355           0 :     return GNUNET_SYSERR;
     356             :   }
     357             : 
     358           0 :   struct GNUNET_PQ_QueryParam params[] = {
     359           0 :     GNUNET_PQ_query_param_uint32 (&num),
     360           0 :     GNUNET_PQ_query_param_string (shard_domain),
     361           0 :     GNUNET_PQ_query_param_string (remote_user),
     362           0 :     GNUNET_PQ_query_param_string (remote_user_pw),
     363             :     GNUNET_PQ_query_param_end
     364             :   };
     365           0 :   struct GNUNET_PQ_ExecuteStatement es[] = {
     366           0 :     GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"),
     367             :     GNUNET_PQ_EXECUTE_STATEMENT_END
     368             :   };
     369           0 :   struct GNUNET_PQ_PreparedStatement ps[] = {
     370           0 :     GNUNET_PQ_make_prepare ("create_foreign_servers",
     371             :                             "SELECT"
     372             :                             " create_foreign_servers"
     373             :                             " ($1, $2, $3, $4);",
     374             :                             4),
     375             :     GNUNET_PQ_PREPARED_STATEMENT_END
     376             :   };
     377             : 
     378           0 :   conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
     379             :                                      "exchangedb-postgres",
     380             :                                      NULL,
     381             :                                      es,
     382             :                                      ps);
     383           0 :   if (NULL == conn)
     384             :   {
     385           0 :     ret = GNUNET_SYSERR;
     386             :   }
     387           0 :   else if (0 > GNUNET_PQ_eval_prepared_non_select (conn,
     388             :                                                    "create_foreign_servers",
     389             :                                                    params))
     390             :   {
     391           0 :     ret = GNUNET_SYSERR;
     392             :   }
     393           0 :   GNUNET_free (shard_domain);
     394           0 :   GNUNET_free (remote_user);
     395           0 :   GNUNET_free (remote_user_pw);
     396           0 :   GNUNET_PQ_disconnect (conn);
     397           0 :   return ret;
     398             : }
     399             : 
     400             : 
     401             : /**
     402             :  * Initialize prepared statements for @a pg.
     403             :  *
     404             :  * @param[in,out] pg connection to initialize
     405             :  * @return #GNUNET_OK on success
     406             :  */
     407             : static enum GNUNET_GenericReturnValue
     408           0 : prepare_statements (struct PostgresClosure *pg)
     409             : {
     410             :   enum GNUNET_GenericReturnValue ret;
     411           0 :   struct GNUNET_PQ_PreparedStatement ps[] = {
     412             :     /* Used in #postgres_insert_denomination_info() and
     413             :      #postgres_add_denomination_key() */
     414           0 :     GNUNET_PQ_make_prepare (
     415             :       "denomination_insert",
     416             :       "INSERT INTO denominations "
     417             :       "(denom_pub_hash"
     418             :       ",denom_pub"
     419             :       ",master_sig"
     420             :       ",valid_from"
     421             :       ",expire_withdraw"
     422             :       ",expire_deposit"
     423             :       ",expire_legal"
     424             :       ",coin_val"                                                /* value of this denom */
     425             :       ",coin_frac"                                                /* fractional value of this denom */
     426             :       ",fee_withdraw_val"
     427             :       ",fee_withdraw_frac"
     428             :       ",fee_deposit_val"
     429             :       ",fee_deposit_frac"
     430             :       ",fee_refresh_val"
     431             :       ",fee_refresh_frac"
     432             :       ",fee_refund_val"
     433             :       ",fee_refund_frac"
     434             :       ",age_mask"
     435             :       ") VALUES "
     436             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
     437             :       " $11, $12, $13, $14, $15, $16, $17, $18);",
     438             :       18),
     439             :     /* Used in #postgres_iterate_denomination_info() */
     440           0 :     GNUNET_PQ_make_prepare (
     441             :       "denomination_iterate",
     442             :       "SELECT"
     443             :       " master_sig"
     444             :       ",denom_pub_hash"
     445             :       ",valid_from"
     446             :       ",expire_withdraw"
     447             :       ",expire_deposit"
     448             :       ",expire_legal"
     449             :       ",coin_val"                                                /* value of this denom */
     450             :       ",coin_frac"                                                /* fractional value of this denom */
     451             :       ",fee_withdraw_val"
     452             :       ",fee_withdraw_frac"
     453             :       ",fee_deposit_val"
     454             :       ",fee_deposit_frac"
     455             :       ",fee_refresh_val"
     456             :       ",fee_refresh_frac"
     457             :       ",fee_refund_val"
     458             :       ",fee_refund_frac"
     459             :       ",denom_pub"
     460             :       ",age_mask"
     461             :       " FROM denominations;",
     462             :       0),
     463             :     /* Used in #postgres_iterate_denominations() */
     464           0 :     GNUNET_PQ_make_prepare (
     465             :       "select_denominations",
     466             :       "SELECT"
     467             :       " denominations.master_sig"
     468             :       ",denom_revocations_serial_id IS NOT NULL AS revoked"
     469             :       ",valid_from"
     470             :       ",expire_withdraw"
     471             :       ",expire_deposit"
     472             :       ",expire_legal"
     473             :       ",coin_val"                                                /* value of this denom */
     474             :       ",coin_frac"                                                /* fractional value of this denom */
     475             :       ",fee_withdraw_val"
     476             :       ",fee_withdraw_frac"
     477             :       ",fee_deposit_val"
     478             :       ",fee_deposit_frac"
     479             :       ",fee_refresh_val"
     480             :       ",fee_refresh_frac"
     481             :       ",fee_refund_val"
     482             :       ",fee_refund_frac"
     483             :       ",denom_type"
     484             :       ",age_mask"
     485             :       ",denom_pub"
     486             :       " FROM denominations"
     487             :       " LEFT JOIN "
     488             :       "   denomination_revocations USING (denominations_serial);",
     489             :       0),
     490             :     /* Used in #postgres_iterate_active_signkeys() */
     491           0 :     GNUNET_PQ_make_prepare (
     492             :       "select_signkeys",
     493             :       "SELECT"
     494             :       " master_sig"
     495             :       ",exchange_pub"
     496             :       ",valid_from"
     497             :       ",expire_sign"
     498             :       ",expire_legal"
     499             :       " FROM exchange_sign_keys esk"
     500             :       " WHERE"
     501             :       "   expire_sign > $1"
     502             :       " AND NOT EXISTS "
     503             :       "  (SELECT esk_serial "
     504             :       "     FROM signkey_revocations skr"
     505             :       "    WHERE esk.esk_serial = skr.esk_serial);",
     506             :       1),
     507             :     /* Used in #postgres_iterate_auditor_denominations() */
     508           0 :     GNUNET_PQ_make_prepare (
     509             :       "select_auditor_denoms",
     510             :       "SELECT"
     511             :       " auditors.auditor_pub"
     512             :       ",denominations.denom_pub_hash"
     513             :       ",auditor_denom_sigs.auditor_sig"
     514             :       " FROM auditor_denom_sigs"
     515             :       " JOIN auditors USING (auditor_uuid)"
     516             :       " JOIN denominations USING (denominations_serial)"
     517             :       " WHERE auditors.is_active;",
     518             :       0),
     519             :     /* Used in #postgres_iterate_active_auditors() */
     520           0 :     GNUNET_PQ_make_prepare (
     521             :       "select_auditors",
     522             :       "SELECT"
     523             :       " auditor_pub"
     524             :       ",auditor_url"
     525             :       ",auditor_name"
     526             :       " FROM auditors"
     527             :       " WHERE"
     528             :       "   is_active;",
     529             :       0),
     530             :     /* Used in #postgres_get_denomination_info() */
     531           0 :     GNUNET_PQ_make_prepare (
     532             :       "denomination_get",
     533             :       "SELECT"
     534             :       " master_sig"
     535             :       ",valid_from"
     536             :       ",expire_withdraw"
     537             :       ",expire_deposit"
     538             :       ",expire_legal"
     539             :       ",coin_val"                                                /* value of this denom */
     540             :       ",coin_frac"                                                /* fractional value of this denom */
     541             :       ",fee_withdraw_val"
     542             :       ",fee_withdraw_frac"
     543             :       ",fee_deposit_val"
     544             :       ",fee_deposit_frac"
     545             :       ",fee_refresh_val"
     546             :       ",fee_refresh_frac"
     547             :       ",fee_refund_val"
     548             :       ",fee_refund_frac"
     549             :       ",age_mask"
     550             :       " FROM denominations"
     551             :       " WHERE denom_pub_hash=$1;",
     552             :       1),
     553             :     /* Used in #postgres_insert_denomination_revocation() */
     554           0 :     GNUNET_PQ_make_prepare (
     555             :       "denomination_revocation_insert",
     556             :       "INSERT INTO denomination_revocations "
     557             :       "(denominations_serial"
     558             :       ",master_sig"
     559             :       ") SELECT denominations_serial,$2"
     560             :       "    FROM denominations"
     561             :       "   WHERE denom_pub_hash=$1;",
     562             :       2),
     563             :     /* Used in #postgres_get_denomination_revocation() */
     564           0 :     GNUNET_PQ_make_prepare (
     565             :       "denomination_revocation_get",
     566             :       "SELECT"
     567             :       " master_sig"
     568             :       ",denom_revocations_serial_id"
     569             :       " FROM denomination_revocations"
     570             :       " WHERE denominations_serial="
     571             :       "  (SELECT denominations_serial"
     572             :       "    FROM denominations"
     573             :       "    WHERE denom_pub_hash=$1);",
     574             :       1),
     575             :     /* Used in #postgres_reserves_get_origin() */
     576           0 :     GNUNET_PQ_make_prepare (
     577             :       "get_h_wire_source_of_reserve",
     578             :       "SELECT"
     579             :       " wire_source_h_payto"
     580             :       " FROM reserves_in"
     581             :       " WHERE reserve_pub=$1",
     582             :       1),
     583           0 :     GNUNET_PQ_make_prepare (
     584             :       "get_kyc_h_payto",
     585             :       "SELECT"
     586             :       " wire_target_h_payto"
     587             :       " FROM wire_targets"
     588             :       " WHERE wire_target_h_payto=$1"
     589             :       " LIMIT 1;",
     590             :       1),
     591             :     /* Used in #postgres_insert_partner() */
     592           0 :     GNUNET_PQ_make_prepare (
     593             :       "insert_partner",
     594             :       "INSERT INTO partners"
     595             :       "  (partner_master_pub"
     596             :       "  ,start_date"
     597             :       "  ,end_date"
     598             :       "  ,wad_frequency"
     599             :       "  ,wad_fee_val"
     600             :       "  ,wad_fee_frac"
     601             :       "  ,master_sig"
     602             :       "  ,partner_base_url"
     603             :       "  ) VALUES "
     604             :       "  ($1, $2, $3, $4, $5, $6, $7, $8);",
     605             :       8),
     606             :     /* Used in #setup_wire_target() */
     607           0 :     GNUNET_PQ_make_prepare (
     608             :       "insert_kyc_status",
     609             :       "INSERT INTO wire_targets"
     610             :       "  (wire_target_h_payto"
     611             :       "  ,payto_uri"
     612             :       "  ) VALUES "
     613             :       "  ($1, $2)"
     614             :       " ON CONFLICT DO NOTHING",
     615             :       2),
     616             :     /* Used in #postgres_drain_kyc_alert() */
     617           0 :     GNUNET_PQ_make_prepare (
     618             :       "drain_kyc_alert",
     619             :       "DELETE FROM kyc_alerts"
     620             :       " WHERE trigger_type=$1"
     621             :       "   AND h_payto = "
     622             :       "   (SELECT h_payto "
     623             :       "      FROM kyc_alerts"
     624             :       "     WHERE trigger_type=$1"
     625             :       "     LIMIT 1)"
     626             :       " RETURNING h_payto;",
     627             :       1),
     628             :     /* Used in #postgres_reserves_get() */
     629           0 :     GNUNET_PQ_make_prepare (
     630             :       "reserves_get",
     631             :       "SELECT"
     632             :       " current_balance_val"
     633             :       ",current_balance_frac"
     634             :       ",expiration_date"
     635             :       ",gc_date"
     636             :       " FROM reserves"
     637             :       " WHERE reserve_pub=$1"
     638             :       " LIMIT 1;",
     639             :       1),
     640           0 :     GNUNET_PQ_make_prepare (
     641             :       "reserve_create",
     642             :       "INSERT INTO reserves "
     643             :       "(reserve_pub"
     644             :       ",current_balance_val"
     645             :       ",current_balance_frac"
     646             :       ",expiration_date"
     647             :       ",gc_date"
     648             :       ") VALUES "
     649             :       "($1, $2, $3, $4, $5)"
     650             :       " ON CONFLICT DO NOTHING"
     651             :       " RETURNING reserve_uuid;",
     652             :       5),
     653             :     /* Used in #postgres_insert_reserve_closed() */
     654           0 :     GNUNET_PQ_make_prepare (
     655             :       "reserves_close_insert",
     656             :       "INSERT INTO reserves_close "
     657             :       "(reserve_pub"
     658             :       ",execution_date"
     659             :       ",wtid"
     660             :       ",wire_target_h_payto"
     661             :       ",amount_val"
     662             :       ",amount_frac"
     663             :       ",closing_fee_val"
     664             :       ",closing_fee_frac"
     665             :       ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8);",
     666             :       8),
     667             :     /* Used in #postgres_insert_drain_profit() */
     668           0 :     GNUNET_PQ_make_prepare (
     669             :       "drain_profit_insert",
     670             :       "INSERT INTO profit_drains "
     671             :       "(wtid"
     672             :       ",account_section"
     673             :       ",payto_uri"
     674             :       ",trigger_date"
     675             :       ",amount_val"
     676             :       ",amount_frac"
     677             :       ",master_sig"
     678             :       ") VALUES ($1, $2, $3, $4, $5, $6, $7);",
     679             :       7),
     680             :     /* Used in #postgres_profit_drains_get_pending() */
     681           0 :     GNUNET_PQ_make_prepare (
     682             :       "get_ready_profit_drain",
     683             :       "SELECT"
     684             :       " profit_drain_serial_id"
     685             :       ",wtid"
     686             :       ",account_section"
     687             :       ",payto_uri"
     688             :       ",trigger_date"
     689             :       ",amount_val"
     690             :       ",amount_frac"
     691             :       ",master_sig"
     692             :       " FROM profit_drains"
     693             :       " WHERE NOT executed"
     694             :       " ORDER BY trigger_date ASC;",
     695             :       0),
     696             :     /* Used in #postgres_profit_drains_get() */
     697           0 :     GNUNET_PQ_make_prepare (
     698             :       "get_profit_drain",
     699             :       "SELECT"
     700             :       " profit_drain_serial_id"
     701             :       ",account_section"
     702             :       ",payto_uri"
     703             :       ",trigger_date"
     704             :       ",amount_val"
     705             :       ",amount_frac"
     706             :       ",master_sig"
     707             :       " FROM profit_drains"
     708             :       " WHERE wtid=$1;",
     709             :       1),
     710             :     /* Used in #postgres_profit_drains_set_finished() */
     711           0 :     GNUNET_PQ_make_prepare (
     712             :       "drain_profit_set_finished",
     713             :       "UPDATE profit_drains"
     714             :       " SET"
     715             :       " executed=TRUE"
     716             :       " WHERE profit_drain_serial_id=$1;",
     717             :       1),
     718             :     /* Used in #postgres_reserves_update() when the reserve is updated */
     719           0 :     GNUNET_PQ_make_prepare (
     720             :       "reserve_update",
     721             :       "UPDATE reserves"
     722             :       " SET"
     723             :       " expiration_date=$1"
     724             :       ",gc_date=$2"
     725             :       ",current_balance_val=$3"
     726             :       ",current_balance_frac=$4"
     727             :       " WHERE reserve_pub=$5;",
     728             :       5),
     729             :     /* Used in #postgres_reserves_in_insert() to store transaction details */
     730           0 :     GNUNET_PQ_make_prepare (
     731             :       "reserves_in_add_transaction",
     732             :       "INSERT INTO reserves_in "
     733             :       "(reserve_pub"
     734             :       ",wire_reference"
     735             :       ",credit_val"
     736             :       ",credit_frac"
     737             :       ",exchange_account_section"
     738             :       ",wire_source_h_payto"
     739             :       ",execution_date"
     740             :       ") VALUES ($1, $2, $3, $4, $5, $6, $7)"
     741             :       " ON CONFLICT DO NOTHING;",
     742             :       7),
     743             :     /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
     744             :        transactions for reserves with serial id '\geq' the given parameter */
     745           0 :     GNUNET_PQ_make_prepare (
     746             :       "audit_reserves_in_get_transactions_incr",
     747             :       "SELECT"
     748             :       " reserves.reserve_pub"
     749             :       ",wire_reference"
     750             :       ",credit_val"
     751             :       ",credit_frac"
     752             :       ",execution_date"
     753             :       ",payto_uri AS sender_account_details"
     754             :       ",reserve_in_serial_id"
     755             :       " FROM reserves_in"
     756             :       " JOIN reserves"
     757             :       "   USING (reserve_pub)"
     758             :       " JOIN wire_targets"
     759             :       "   ON (wire_source_h_payto = wire_target_h_payto)"
     760             :       " WHERE reserve_in_serial_id>=$1"
     761             :       " ORDER BY reserve_in_serial_id;",
     762             :       1),
     763             :     /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
     764             :        transactions for reserves with serial id '\geq' the given parameter */
     765           0 :     GNUNET_PQ_make_prepare (
     766             :       "audit_reserves_in_get_transactions_incr_by_account",
     767             :       "SELECT"
     768             :       " reserves.reserve_pub"
     769             :       ",wire_reference"
     770             :       ",credit_val"
     771             :       ",credit_frac"
     772             :       ",execution_date"
     773             :       ",payto_uri AS sender_account_details"
     774             :       ",reserve_in_serial_id"
     775             :       " FROM reserves_in"
     776             :       " JOIN reserves "
     777             :       "   USING (reserve_pub)"
     778             :       " JOIN wire_targets"
     779             :       "   ON (wire_source_h_payto = wire_target_h_payto)"
     780             :       " WHERE reserve_in_serial_id>=$1 AND exchange_account_section=$2"
     781             :       " ORDER BY reserve_in_serial_id;",
     782             :       2),
     783             :     /* Used in #postgres_get_reserve_history() to obtain inbound transactions
     784             :        for a reserve */
     785           0 :     GNUNET_PQ_make_prepare (
     786             :       "reserves_in_get_transactions",
     787             :       /*
     788             :       "SELECT"
     789             :       " wire_reference"
     790             :       ",credit_val"
     791             :       ",credit_frac"
     792             :       ",execution_date"
     793             :       ",payto_uri AS sender_account_details"
     794             :       " FROM reserves_in"
     795             :       " JOIN wire_targets"
     796             :       "   ON (wire_source_h_payto = wire_target_h_payto)"
     797             :       " WHERE reserve_pub=$1;",
     798             :       */
     799             :       "WITH ri AS MATERIALIZED ( "
     800             :       "  SELECT * "
     801             :       "  FROM reserves_in "
     802             :       "  WHERE reserve_pub = $1 "
     803             :       ") "
     804             :       "SELECT  "
     805             :       "  wire_reference "
     806             :       "  ,credit_val "
     807             :       "  ,credit_frac "
     808             :       "  ,execution_date "
     809             :       "  ,payto_uri AS sender_account_details "
     810             :       "FROM wire_targets "
     811             :       "JOIN ri  "
     812             :       "  ON (wire_target_h_payto = wire_source_h_payto) "
     813             :       "WHERE wire_target_h_payto = ( "
     814             :       "  SELECT wire_source_h_payto FROM ri "
     815             :       "); ",
     816             :       1),
     817             :     /* Used in #postgres_get_reserve_status() to obtain inbound transactions
     818             :        for a reserve */
     819           0 :     GNUNET_PQ_make_prepare (
     820             :       "reserves_in_get_transactions_truncated",
     821             :       /*
     822             :       "SELECT"
     823             :       " wire_reference"
     824             :       ",credit_val"
     825             :       ",credit_frac"
     826             :       ",execution_date"
     827             :       ",payto_uri AS sender_account_details"
     828             :       " FROM reserves_in"
     829             :       " JOIN wire_targets"
     830             :       "   ON (wire_source_h_payto = wire_target_h_payto)"
     831             :       " WHERE reserve_pub=$1"
     832             :       "   AND execution_date>=$2;",
     833             :       */
     834             :       "WITH ri AS MATERIALIZED ( "
     835             :       "  SELECT * "
     836             :       "  FROM reserves_in "
     837             :       "  WHERE reserve_pub = $1 "
     838             :       ") "
     839             :       "SELECT  "
     840             :       "  wire_reference "
     841             :       "  ,credit_val "
     842             :       "  ,credit_frac "
     843             :       "  ,execution_date "
     844             :       "  ,payto_uri AS sender_account_details "
     845             :       "FROM wire_targets "
     846             :       "JOIN ri  "
     847             :       "  ON (wire_target_h_payto = wire_source_h_payto) "
     848             :       "WHERE execution_date >= $2"
     849             :       "  AND wire_target_h_payto = ( "
     850             :       "  SELECT wire_source_h_payto FROM ri "
     851             :       "); ",
     852             :       2),
     853             :     /* Used in #postgres_do_withdraw() to store
     854             :        the signature of a blinded coin with the blinded coin's
     855             :        details before returning it during /reserve/withdraw. We store
     856             :        the coin's denomination information (public key, signature)
     857             :        and the blinded message as well as the reserve that the coin
     858             :        is being withdrawn from and the signature of the message
     859             :        authorizing the withdrawal. */
     860           0 :     GNUNET_PQ_make_prepare (
     861             :       "call_withdraw",
     862             :       "SELECT "
     863             :       " reserve_found"
     864             :       ",balance_ok"
     865             :       ",nonce_ok"
     866             :       ",ruuid"
     867             :       " FROM exchange_do_withdraw"
     868             :       " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);",
     869             :       10),
     870             :     /* Used in #postgres_do_batch_withdraw() to
     871             :        update the reserve balance and check its status */
     872           0 :     GNUNET_PQ_make_prepare (
     873             :       "call_batch_withdraw",
     874             :       "SELECT "
     875             :       " reserve_found"
     876             :       ",balance_ok"
     877             :       ",ruuid"
     878             :       " FROM exchange_do_batch_withdraw"
     879             :       " ($1,$2,$3,$4,$5);",
     880             :       5),
     881             :     /* Used in #postgres_do_batch_withdraw_insert() to store
     882             :        the signature of a blinded coin with the blinded coin's
     883             :        details. */
     884           0 :     GNUNET_PQ_make_prepare (
     885             :       "call_batch_withdraw_insert",
     886             :       "SELECT "
     887             :       " out_denom_unknown AS denom_unknown"
     888             :       ",out_conflict AS conflict"
     889             :       ",out_nonce_reuse AS nonce_reuse"
     890             :       " FROM exchange_do_batch_withdraw_insert"
     891             :       " ($1,$2,$3,$4,$5,$6,$7,$8,$9);",
     892             :       9),
     893             :     /* Used in #postgres_do_deposit() to execute a deposit,
     894             :        checking the coin's balance in the process as needed. */
     895           0 :     GNUNET_PQ_make_prepare (
     896             :       "call_deposit",
     897             :       "SELECT "
     898             :       " out_exchange_timestamp AS exchange_timestamp"
     899             :       ",out_balance_ok AS balance_ok"
     900             :       ",out_conflict AS conflicted"
     901             :       " FROM exchange_do_deposit"
     902             :       " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17);",
     903             :       17),
     904             :     /* used in postgres_do_purse_deposit() */
     905           0 :     GNUNET_PQ_make_prepare (
     906             :       "call_purse_deposit",
     907             :       "SELECT "
     908             :       " out_balance_ok AS balance_ok"
     909             :       ",out_conflict AS conflict"
     910             :       " FROM exchange_do_purse_deposit"
     911             :       " ($1,$2,$3,$4,$5,$6,$7,$8,$9);",
     912             :       9),
     913             :     /* Used in #postgres_update_aggregation_transient() */
     914           0 :     GNUNET_PQ_make_prepare (
     915             :       "set_purse_balance",
     916             :       "UPDATE purse_requests"
     917             :       " SET balance_val=$2"
     918             :       "    ,balance_frac=$3"
     919             :       " WHERE purse_pub=$1;",
     920             :       3),
     921             :     /* used in #postgres_expire_purse() */
     922           0 :     GNUNET_PQ_make_prepare (
     923             :       "call_expire_purse",
     924             :       "SELECT "
     925             :       " out_found AS found"
     926             :       " FROM exchange_do_expire_purse"
     927             :       " ($1,$2);",
     928             :       2),
     929             :     /* Used in #postgres_do_melt() to melt a coin. */
     930           0 :     GNUNET_PQ_make_prepare (
     931             :       "call_melt",
     932             :       "SELECT "
     933             :       " out_balance_ok AS balance_ok"
     934             :       ",out_zombie_bad AS zombie_required"
     935             :       ",out_noreveal_index AS noreveal_index"
     936             :       " FROM exchange_do_melt"
     937             :       " ($1,$2,$3,$4,$5,$6,$7,$8,$9);",
     938             :       9),
     939             :     /* Used in #postgres_do_refund() to refund a deposit. */
     940           0 :     GNUNET_PQ_make_prepare (
     941             :       "call_refund",
     942             :       "SELECT "
     943             :       " out_not_found AS not_found"
     944             :       ",out_refund_ok AS refund_ok"
     945             :       ",out_gone AS gone"
     946             :       ",out_conflict AS conflict"
     947             :       " FROM exchange_do_refund"
     948             :       " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13);",
     949             :       13),
     950             :     /* Used in #postgres_do_recoup() to recoup a coin to a reserve. */
     951           0 :     GNUNET_PQ_make_prepare (
     952             :       "call_recoup",
     953             :       "SELECT "
     954             :       " out_recoup_timestamp AS recoup_timestamp"
     955             :       ",out_recoup_ok AS recoup_ok"
     956             :       ",out_internal_failure AS internal_failure"
     957             :       " FROM exchange_do_recoup_to_reserve"
     958             :       " ($1,$2,$3,$4,$5,$6,$7,$8,$9);",
     959             :       9),
     960             :     /* Used in #postgres_do_recoup_refresh() to recoup a coin to a zombie coin. */
     961           0 :     GNUNET_PQ_make_prepare (
     962             :       "call_recoup_refresh",
     963             :       "SELECT "
     964             :       " out_recoup_timestamp AS recoup_timestamp"
     965             :       ",out_recoup_ok AS recoup_ok"
     966             :       ",out_internal_failure AS internal_failure"
     967             :       " FROM exchange_do_recoup_to_coin"
     968             :       " ($1,$2,$3,$4,$5,$6,$7);",
     969             :       7),
     970             :     /* Used in #postgres_get_withdraw_info() to
     971             :        locate the response for a /reserve/withdraw request
     972             :        using the hash of the blinded message.  Used to
     973             :        make sure /reserve/withdraw requests are idempotent. */
     974           0 :     GNUNET_PQ_make_prepare (
     975             :       "get_withdraw_info",
     976             :       "SELECT"
     977             :       " denom.denom_pub_hash"
     978             :       ",denom_sig"
     979             :       ",reserve_sig"
     980             :       ",reserves.reserve_pub"
     981             :       ",execution_date"
     982             :       ",h_blind_ev"
     983             :       ",amount_with_fee_val"
     984             :       ",amount_with_fee_frac"
     985             :       ",denom.fee_withdraw_val"
     986             :       ",denom.fee_withdraw_frac"
     987             :       " FROM reserves_out"
     988             :       "    JOIN reserves"
     989             :       "      USING (reserve_uuid)"
     990             :       "    JOIN denominations denom"
     991             :       "      USING (denominations_serial)"
     992             :       " WHERE h_blind_ev=$1;",
     993             :       1),
     994             :     /* Used during #postgres_get_reserve_history() to
     995             :        obtain all of the /reserve/withdraw operations that
     996             :        have been performed on a given reserve. (i.e. to
     997             :        demonstrate double-spending) */
     998           0 :     GNUNET_PQ_make_prepare (
     999             :       "get_reserves_out",
    1000             :       /*
    1001             :       "SELECT"
    1002             :       " ro.h_blind_ev"
    1003             :       ",denom.denom_pub_hash"
    1004             :       ",ro.denom_sig"
    1005             :       ",ro.reserve_sig"
    1006             :       ",ro.execution_date"
    1007             :       ",ro.amount_with_fee_val"
    1008             :       ",ro.amount_with_fee_frac"
    1009             :       ",denom.fee_withdraw_val"
    1010             :       ",denom.fee_withdraw_frac"
    1011             :       " FROM reserves res"
    1012             :       " JOIN reserves_out_by_reserve ror"
    1013             :       "   ON (res.reserve_uuid = ror.reserve_uuid)"
    1014             :       " JOIN reserves_out ro"
    1015             :       "   ON (ro.h_blind_ev = ror.h_blind_ev)"
    1016             :       " JOIN denominations denom"
    1017             :       "   ON (ro.denominations_serial = denom.denominations_serial)"
    1018             :       " WHERE res.reserve_pub=$1;",
    1019             :       */
    1020             :       "WITH robr AS MATERIALIZED ( "
    1021             :       "  SELECT h_blind_ev "
    1022             :       "  FROM reserves_out_by_reserve "
    1023             :       "  WHERE reserve_uuid= ( "
    1024             :       "    SELECT reserve_uuid "
    1025             :       "    FROM reserves "
    1026             :       "    WHERE reserve_pub = $1 "
    1027             :       "  ) "
    1028             :       ") SELECT "
    1029             :       "  ro.h_blind_ev "
    1030             :       "  ,denom.denom_pub_hash "
    1031             :       "  ,ro.denom_sig "
    1032             :       "  ,ro.reserve_sig "
    1033             :       "  ,ro.execution_date "
    1034             :       "  ,ro.amount_with_fee_val "
    1035             :       "  ,ro.amount_with_fee_frac "
    1036             :       "  ,denom.fee_withdraw_val "
    1037             :       "  ,denom.fee_withdraw_frac "
    1038             :       "FROM robr "
    1039             :       "JOIN reserves_out ro "
    1040             :       "  ON (ro.h_blind_ev = robr.h_blind_ev) "
    1041             :       "JOIN denominations denom "
    1042             :       "  ON (ro.denominations_serial = denom.denominations_serial); ",
    1043             :       1),
    1044             :     /* Used during #postgres_get_reserve_status() to
    1045             :        obtain all of the /reserve/withdraw operations that
    1046             :        have been performed on a given reserve. (i.e. to
    1047             :        demonstrate double-spending) */
    1048           0 :     GNUNET_PQ_make_prepare (
    1049             :       "get_reserves_out_truncated",
    1050             :       /*
    1051             :       "SELECT"
    1052             :       " ro.h_blind_ev"
    1053             :       ",denom.denom_pub_hash"
    1054             :       ",ro.denom_sig"
    1055             :       ",ro.reserve_sig"
    1056             :       ",ro.execution_date"
    1057             :       ",ro.amount_with_fee_val"
    1058             :       ",ro.amount_with_fee_frac"
    1059             :       ",denom.fee_withdraw_val"
    1060             :       ",denom.fee_withdraw_frac"
    1061             :       " FROM reserves res"
    1062             :       " JOIN reserves_out_by_reserve ror"
    1063             :       "   ON (res.reserve_uuid = ror.reserve_uuid)"
    1064             :       " JOIN reserves_out ro"
    1065             :       "   ON (ro.h_blind_ev = ror.h_blind_ev)"
    1066             :       " JOIN denominations denom"
    1067             :       "   ON (ro.denominations_serial = denom.denominations_serial)"
    1068             :       " WHERE res.reserve_pub=$1"
    1069             :       "   AND execution_date>=$2;",
    1070             :       */
    1071             :       "WITH robr AS MATERIALIZED ( "
    1072             :       "  SELECT h_blind_ev "
    1073             :       "  FROM reserves_out_by_reserve "
    1074             :       "  WHERE reserve_uuid= ( "
    1075             :       "    SELECT reserve_uuid "
    1076             :       "    FROM reserves "
    1077             :       "    WHERE reserve_pub = $1 "
    1078             :       "  ) "
    1079             :       ") SELECT "
    1080             :       "  ro.h_blind_ev "
    1081             :       "  ,denom.denom_pub_hash "
    1082             :       "  ,ro.denom_sig "
    1083             :       "  ,ro.reserve_sig "
    1084             :       "  ,ro.execution_date "
    1085             :       "  ,ro.amount_with_fee_val "
    1086             :       "  ,ro.amount_with_fee_frac "
    1087             :       "  ,denom.fee_withdraw_val "
    1088             :       "  ,denom.fee_withdraw_frac "
    1089             :       "FROM robr "
    1090             :       "JOIN reserves_out ro "
    1091             :       "  ON (ro.h_blind_ev = robr.h_blind_ev) "
    1092             :       "JOIN denominations denom "
    1093             :       "  ON (ro.denominations_serial = denom.denominations_serial)"
    1094             :       " WHERE ro.execution_date>=$2;",
    1095             :       2),
    1096             :     /* Used in #postgres_select_withdrawals_above_serial_id() */
    1097             : 
    1098           0 :     GNUNET_PQ_make_prepare (
    1099             :       "get_reserve_balance",
    1100             :       "SELECT"
    1101             :       " current_balance_val"
    1102             :       ",current_balance_frac"
    1103             :       " FROM reserves"
    1104             :       " WHERE reserve_pub=$1;",
    1105             :       1),
    1106             :     /* Fetch deposits with rowid '\geq' the given parameter */
    1107             : 
    1108           0 :     GNUNET_PQ_make_prepare (
    1109             :       "audit_get_reserves_out_incr",
    1110             :       "SELECT"
    1111             :       " h_blind_ev"
    1112             :       ",denom.denom_pub"
    1113             :       ",reserve_sig"
    1114             :       ",reserves.reserve_pub"
    1115             :       ",execution_date"
    1116             :       ",amount_with_fee_val"
    1117             :       ",amount_with_fee_frac"
    1118             :       ",reserve_out_serial_id"
    1119             :       " FROM reserves_out"
    1120             :       "    JOIN reserves"
    1121             :       "      USING (reserve_uuid)"
    1122             :       "    JOIN denominations denom"
    1123             :       "      USING (denominations_serial)"
    1124             :       " WHERE reserve_out_serial_id>=$1"
    1125             :       " ORDER BY reserve_out_serial_id ASC;",
    1126             :       1),
    1127             : 
    1128             :     /* Used in #postgres_count_known_coins() */
    1129           0 :     GNUNET_PQ_make_prepare (
    1130             :       "count_known_coins",
    1131             :       "SELECT"
    1132             :       " COUNT(*) AS count"
    1133             :       " FROM known_coins"
    1134             :       " WHERE denominations_serial="
    1135             :       "  (SELECT denominations_serial"
    1136             :       "    FROM denominations"
    1137             :       "    WHERE denom_pub_hash=$1);",
    1138             :       1),
    1139             :     /* Used in #postgres_get_known_coin() to fetch
    1140             :        the denomination public key and signature for
    1141             :        a coin known to the exchange. */
    1142           0 :     GNUNET_PQ_make_prepare (
    1143             :       "get_known_coin",
    1144             :       "SELECT"
    1145             :       " denominations.denom_pub_hash"
    1146             :       ",age_commitment_hash"
    1147             :       ",denom_sig"
    1148             :       " FROM known_coins"
    1149             :       " JOIN denominations USING (denominations_serial)"
    1150             :       " WHERE coin_pub=$1;",
    1151             :       1),
    1152             :     /* Used in #postgres_ensure_coin_known() */
    1153           0 :     GNUNET_PQ_make_prepare (
    1154             :       "get_known_coin_dh",
    1155             :       "SELECT"
    1156             :       " denominations.denom_pub_hash"
    1157             :       " FROM known_coins"
    1158             :       " JOIN denominations USING (denominations_serial)"
    1159             :       " WHERE coin_pub=$1;",
    1160             :       1),
    1161             :     /* Used in #postgres_get_coin_denomination() to fetch
    1162             :        the denomination public key hash for
    1163             :        a coin known to the exchange. */
    1164           0 :     GNUNET_PQ_make_prepare (
    1165             :       "get_coin_denomination",
    1166             :       "SELECT"
    1167             :       " denominations.denom_pub_hash"
    1168             :       ",known_coin_id"
    1169             :       " FROM known_coins"
    1170             :       " JOIN denominations USING (denominations_serial)"
    1171             :       " WHERE coin_pub=$1"
    1172             :       " FOR SHARE;",
    1173             :       1),
    1174             :     /* Used in #postgres_insert_known_coin() to store the denomination public
    1175             :        key and signature for a coin known to the exchange.
    1176             : 
    1177             :        See also:
    1178             :        https://stackoverflow.com/questions/34708509/how-to-use-returning-with-on-conflict-in-postgresql/37543015#37543015
    1179             :      */
    1180           0 :     GNUNET_PQ_make_prepare (
    1181             :       "insert_known_coin",
    1182             :       "WITH dd"
    1183             :       "  (denominations_serial"
    1184             :       "  ,coin_val"
    1185             :       "  ,coin_frac"
    1186             :       "  ) AS ("
    1187             :       "    SELECT "
    1188             :       "       denominations_serial"
    1189             :       "      ,coin_val"
    1190             :       "      ,coin_frac"
    1191             :       "        FROM denominations"
    1192             :       "        WHERE denom_pub_hash=$2"
    1193             :       "  ), input_rows"
    1194             :       "    (coin_pub) AS ("
    1195             :       "      VALUES ($1::BYTEA)"
    1196             :       "  ), ins AS ("
    1197             :       "  INSERT INTO known_coins "
    1198             :       "  (coin_pub"
    1199             :       "  ,denominations_serial"
    1200             :       "  ,age_commitment_hash"
    1201             :       "  ,denom_sig"
    1202             :       "  ,remaining_val"
    1203             :       "  ,remaining_frac"
    1204             :       "  ) SELECT "
    1205             :       "     $1"
    1206             :       "    ,denominations_serial"
    1207             :       "    ,$3"
    1208             :       "    ,$4"
    1209             :       "    ,coin_val"
    1210             :       "    ,coin_frac"
    1211             :       "  FROM dd"
    1212             :       "  ON CONFLICT DO NOTHING" /* CONFLICT on (coin_pub) */
    1213             :       "  RETURNING "
    1214             :       "     known_coin_id"
    1215             :       "  ) "
    1216             :       "SELECT "
    1217             :       "   FALSE AS existed"
    1218             :       "  ,known_coin_id"
    1219             :       "  ,NULL AS denom_pub_hash"
    1220             :       "  ,NULL AS age_commitment_hash"
    1221             :       "  FROM ins "
    1222             :       "UNION ALL "
    1223             :       "SELECT "
    1224             :       "   TRUE AS existed"
    1225             :       "  ,known_coin_id"
    1226             :       "  ,denom_pub_hash"
    1227             :       "  ,kc.age_commitment_hash"
    1228             :       "  FROM input_rows"
    1229             :       "  JOIN known_coins kc USING (coin_pub)"
    1230             :       "  JOIN denominations USING (denominations_serial)"
    1231             :       "  LIMIT 1",
    1232             :       4),
    1233             : 
    1234             :     /* Used in #postgres_get_melt() to fetch
    1235             :        high-level information about a melt operation */
    1236           0 :     GNUNET_PQ_make_prepare (
    1237             :       "get_melt",
    1238             :       /* "SELECT"
    1239             :       " denoms.denom_pub_hash"
    1240             :       ",denoms.fee_refresh_val"
    1241             :       ",denoms.fee_refresh_frac"
    1242             :       ",old_coin_pub"
    1243             :       ",old_coin_sig"
    1244             :       ",kc.age_commitment_hash"
    1245             :       ",amount_with_fee_val"
    1246             :       ",amount_with_fee_frac"
    1247             :       ",noreveal_index"
    1248             :       ",melt_serial_id"
    1249             :       " FROM refresh_commitments"
    1250             :       "   JOIN known_coins kc"
    1251             :       "     ON (old_coin_pub = kc.coin_pub)"
    1252             :       "   JOIN denominations denoms"
    1253             :       "     ON (kc.denominations_serial = denoms.denominations_serial)"
    1254             :       " WHERE rc=$1;", */
    1255             :       "WITH rc AS MATERIALIZED ( "
    1256             :       " SELECT"
    1257             :       "  * FROM refresh_commitments"
    1258             :       " WHERE rc=$1"
    1259             :       ")"
    1260             :       "SELECT"
    1261             :       " denoms.denom_pub_hash"
    1262             :       ",denoms.fee_refresh_val"
    1263             :       ",denoms.fee_refresh_frac"
    1264             :       ",rc.old_coin_pub"
    1265             :       ",rc.old_coin_sig"
    1266             :       ",kc.age_commitment_hash"
    1267             :       ",amount_with_fee_val"
    1268             :       ",amount_with_fee_frac"
    1269             :       ",noreveal_index"
    1270             :       ",melt_serial_id "
    1271             :       "FROM ("
    1272             :       " SELECT"
    1273             :       "  * "
    1274             :       " FROM known_coins"
    1275             :       " WHERE coin_pub=(SELECT old_coin_pub from rc)"
    1276             :       ") kc "
    1277             :       "JOIN rc"
    1278             :       "  ON (kc.coin_pub=rc.old_coin_pub) "
    1279             :       "JOIN denominations denoms"
    1280             :       "  USING (denominations_serial);",
    1281             :       1),
    1282             :     /* Used in #postgres_select_refreshes_above_serial_id() to fetch
    1283             :        refresh session with id '\geq' the given parameter */
    1284           0 :     GNUNET_PQ_make_prepare (
    1285             :       "audit_get_refresh_commitments_incr",
    1286             :       "SELECT"
    1287             :       " denom.denom_pub"
    1288             :       ",kc.coin_pub AS old_coin_pub"
    1289             :       ",kc.age_commitment_hash"
    1290             :       ",old_coin_sig"
    1291             :       ",amount_with_fee_val"
    1292             :       ",amount_with_fee_frac"
    1293             :       ",noreveal_index"
    1294             :       ",melt_serial_id"
    1295             :       ",rc"
    1296             :       " FROM refresh_commitments"
    1297             :       "   JOIN known_coins kc"
    1298             :       "     ON (refresh_commitments.old_coin_pub = kc.coin_pub)"
    1299             :       "   JOIN denominations denom"
    1300             :       "     ON (kc.denominations_serial = denom.denominations_serial)"
    1301             :       " WHERE melt_serial_id>=$1"
    1302             :       " ORDER BY melt_serial_id ASC;",
    1303             :       1),
    1304             :     /* Query the 'refresh_commitments' by coin public key,
    1305             :        used in #postgres_get_coin_transactions() */
    1306           0 :     GNUNET_PQ_make_prepare (
    1307             :       "get_refresh_session_by_coin",
    1308             :       "SELECT"
    1309             :       " rc"
    1310             :       ",old_coin_sig"
    1311             :       ",amount_with_fee_val"
    1312             :       ",amount_with_fee_frac"
    1313             :       ",denoms.denom_pub_hash"
    1314             :       ",denoms.fee_refresh_val"
    1315             :       ",denoms.fee_refresh_frac"
    1316             :       ",kc.age_commitment_hash"
    1317             :       ",melt_serial_id"
    1318             :       " FROM refresh_commitments"
    1319             :       " JOIN known_coins kc"
    1320             :       "   ON (refresh_commitments.old_coin_pub = kc.coin_pub)"
    1321             :       " JOIN denominations denoms"
    1322             :       "   USING (denominations_serial)"
    1323             :       " WHERE old_coin_pub=$1;",
    1324             :       1),
    1325             :     /* Find purse deposits by coin,
    1326             :        used in #postgres_get_coin_transactions() */
    1327           0 :     GNUNET_PQ_make_prepare (
    1328             :       "get_purse_deposit_by_coin_pub",
    1329             :       "SELECT"
    1330             :       " partner_base_url"
    1331             :       ",pd.amount_with_fee_val"
    1332             :       ",pd.amount_with_fee_frac"
    1333             :       ",denoms.fee_deposit_val"
    1334             :       ",denoms.fee_deposit_frac"
    1335             :       ",pd.purse_pub"
    1336             :       ",kc.age_commitment_hash"
    1337             :       ",pd.coin_sig"
    1338             :       ",pd.purse_deposit_serial_id"
    1339             :       ",pr.refunded"
    1340             :       " FROM purse_deposits pd"
    1341             :       " LEFT JOIN partners"
    1342             :       "   USING (partner_serial_id)"
    1343             :       " JOIN purse_requests pr"
    1344             :       "   USING (purse_pub)"
    1345             :       " JOIN known_coins kc"
    1346             :       "   ON (pd.coin_pub = kc.coin_pub)"
    1347             :       " JOIN denominations denoms"
    1348             :       "   USING (denominations_serial)"
    1349             :       // FIXME: use to-be-created materialized index
    1350             :       // on coin_pub (query crosses partitions!)
    1351             :       " WHERE pd.coin_pub=$1;",
    1352             :       1),
    1353             :     /* Store information about the desired denominations for a
    1354             :        refresh operation, used in #postgres_insert_refresh_reveal() */
    1355           0 :     GNUNET_PQ_make_prepare (
    1356             :       "insert_refresh_revealed_coin",
    1357             :       "INSERT INTO refresh_revealed_coins "
    1358             :       "(melt_serial_id "
    1359             :       ",freshcoin_index "
    1360             :       ",link_sig "
    1361             :       ",denominations_serial "
    1362             :       ",coin_ev"
    1363             :       ",ewv"
    1364             :       ",h_coin_ev"
    1365             :       ",ev_sig"
    1366             :       ") SELECT $1, $2, $3, "
    1367             :       "         denominations_serial, $5, $6, $7, $8"
    1368             :       "    FROM denominations"
    1369             :       "   WHERE denom_pub_hash=$4"
    1370             :       " ON CONFLICT DO NOTHING;",
    1371             :       8),
    1372             :     /* Obtain information about the coins created in a refresh
    1373             :        operation, used in #postgres_get_refresh_reveal() */
    1374           0 :     GNUNET_PQ_make_prepare (
    1375             :       "get_refresh_revealed_coins",
    1376             :       "SELECT "
    1377             :       " rrc.freshcoin_index"
    1378             :       ",denom.denom_pub_hash"
    1379             :       ",rrc.h_coin_ev"
    1380             :       ",rrc.link_sig"
    1381             :       ",rrc.coin_ev"
    1382             :       ",rrc.ewv"
    1383             :       ",rrc.ev_sig"
    1384             :       " FROM refresh_commitments"
    1385             :       "    JOIN refresh_revealed_coins rrc"
    1386             :       "      USING (melt_serial_id)"
    1387             :       "    JOIN denominations denom "
    1388             :       "      USING (denominations_serial)"
    1389             :       " WHERE rc=$1;",
    1390             :       1),
    1391             : 
    1392             :     /* Used in #postgres_insert_refresh_reveal() to store the transfer
    1393             :        keys we learned */
    1394           0 :     GNUNET_PQ_make_prepare (
    1395             :       "insert_refresh_transfer_keys",
    1396             :       "INSERT INTO refresh_transfer_keys "
    1397             :       "(melt_serial_id"
    1398             :       ",transfer_pub"
    1399             :       ",transfer_privs"
    1400             :       ") VALUES ($1, $2, $3)"
    1401             :       " ON CONFLICT DO NOTHING;",
    1402             :       3),
    1403             :     /* Used in #postgres_insert_refund() to store refund information */
    1404           0 :     GNUNET_PQ_make_prepare (
    1405             :       "insert_refund",
    1406             :       "INSERT INTO refunds "
    1407             :       "(coin_pub "
    1408             :       ",deposit_serial_id"
    1409             :       ",merchant_sig "
    1410             :       ",rtransaction_id "
    1411             :       ",amount_with_fee_val "
    1412             :       ",amount_with_fee_frac "
    1413             :       ") SELECT $1, deposit_serial_id, $3, $5, $6, $7"
    1414             :       "    FROM deposits"
    1415             :       "   WHERE coin_pub=$1"
    1416             :       "     AND h_contract_terms=$4"
    1417             :       "     AND merchant_pub=$2",
    1418             :       7),
    1419             :     /* Query the 'refunds' by coin public key */
    1420           0 :     GNUNET_PQ_make_prepare (
    1421             :       "get_refunds_by_coin",
    1422             :       "SELECT"
    1423             :       " dep.merchant_pub"
    1424             :       ",ref.merchant_sig"
    1425             :       ",dep.h_contract_terms"
    1426             :       ",ref.rtransaction_id"
    1427             :       ",ref.amount_with_fee_val"
    1428             :       ",ref.amount_with_fee_frac"
    1429             :       ",denom.fee_refund_val "
    1430             :       ",denom.fee_refund_frac "
    1431             :       ",ref.refund_serial_id"
    1432             :       " FROM refunds ref"
    1433             :       " JOIN deposits dep"
    1434             :       "   ON (ref.coin_pub = dep.coin_pub AND ref.deposit_serial_id = dep.deposit_serial_id)"
    1435             :       " JOIN known_coins kc"
    1436             :       "   ON (ref.coin_pub = kc.coin_pub)"
    1437             :       " JOIN denominations denom"
    1438             :       "   USING (denominations_serial)"
    1439             :       " WHERE ref.coin_pub=$1;",
    1440             :       1),
    1441             :     /* Query the 'refunds' by coin public key, merchant_pub and contract hash */
    1442           0 :     GNUNET_PQ_make_prepare (
    1443             :       "get_refunds_by_coin_and_contract",
    1444             :       "SELECT"
    1445             :       " ref.amount_with_fee_val"
    1446             :       ",ref.amount_with_fee_frac"
    1447             :       " FROM refunds ref"
    1448             :       " JOIN deposits dep"
    1449             :       "   USING (coin_pub,deposit_serial_id)"
    1450             :       " WHERE ref.coin_pub=$1"
    1451             :       "   AND dep.merchant_pub=$2"
    1452             :       "   AND dep.h_contract_terms=$3;",
    1453             :       3),
    1454             :     /* Fetch refunds with rowid '\geq' the given parameter */
    1455           0 :     GNUNET_PQ_make_prepare (
    1456             :       "audit_get_refunds_incr",
    1457             :       "SELECT"
    1458             :       " dep.merchant_pub"
    1459             :       ",ref.merchant_sig"
    1460             :       ",dep.h_contract_terms"
    1461             :       ",ref.rtransaction_id"
    1462             :       ",denom.denom_pub"
    1463             :       ",kc.coin_pub"
    1464             :       ",ref.amount_with_fee_val"
    1465             :       ",ref.amount_with_fee_frac"
    1466             :       ",ref.refund_serial_id"
    1467             :       " FROM refunds ref"
    1468             :       "   JOIN deposits dep"
    1469             :       "     ON (ref.coin_pub=dep.coin_pub AND ref.deposit_serial_id=dep.deposit_serial_id)"
    1470             :       "   JOIN known_coins kc"
    1471             :       "     ON (dep.coin_pub=kc.coin_pub)"
    1472             :       "   JOIN denominations denom"
    1473             :       "     ON (kc.denominations_serial=denom.denominations_serial)"
    1474             :       " WHERE ref.refund_serial_id>=$1"
    1475             :       " ORDER BY ref.refund_serial_id ASC;",
    1476             :       1),
    1477           0 :     GNUNET_PQ_make_prepare (
    1478             :       "test_refund_full",
    1479             :       "SELECT"
    1480             :       " CAST(SUM(CAST(ref.amount_with_fee_frac AS INT8)) AS INT8) AS s_f"
    1481             :       ",CAST(SUM(ref.amount_with_fee_val) AS INT8) AS s_v"
    1482             :       ",dep.amount_with_fee_val"
    1483             :       ",dep.amount_with_fee_frac"
    1484             :       " FROM refunds ref"
    1485             :       "   JOIN deposits dep"
    1486             :       "     ON (ref.coin_pub=dep.coin_pub AND ref.deposit_serial_id=dep.deposit_serial_id)"
    1487             :       " WHERE ref.refund_serial_id=$1"
    1488             :       " GROUP BY (dep.amount_with_fee_val, dep.amount_with_fee_frac);",
    1489             :       1),
    1490             : 
    1491             :     /* Store information about a /deposit the exchange is to execute.
    1492             :        Used in #postgres_insert_deposit().  Only used in test cases. */
    1493           0 :     GNUNET_PQ_make_prepare (
    1494             :       "insert_deposit",
    1495             :       "INSERT INTO deposits "
    1496             :       "(known_coin_id"
    1497             :       ",coin_pub"
    1498             :       ",amount_with_fee_val"
    1499             :       ",amount_with_fee_frac"
    1500             :       ",wallet_timestamp"
    1501             :       ",refund_deadline"
    1502             :       ",wire_deadline"
    1503             :       ",merchant_pub"
    1504             :       ",h_contract_terms"
    1505             :       ",wire_salt"
    1506             :       ",wire_target_h_payto"
    1507             :       ",coin_sig"
    1508             :       ",exchange_timestamp"
    1509             :       ",shard"
    1510             :       ") SELECT known_coin_id, $1, $2, $3, $4, $5, $6, "
    1511             :       " $7, $8, $9, $10, $11, $12, $13"
    1512             :       "    FROM known_coins"
    1513             :       "   WHERE coin_pub=$1"
    1514             :       " ON CONFLICT DO NOTHING;",
    1515             :       13),
    1516             :     /* Fetch an existing deposit request, used to ensure idempotency
    1517             :        during /deposit processing. Used in #postgres_have_deposit(). */
    1518           0 :     GNUNET_PQ_make_prepare (
    1519             :       "get_deposit",
    1520             :       "SELECT"
    1521             :       " dep.amount_with_fee_val"
    1522             :       ",dep.amount_with_fee_frac"
    1523             :       ",denominations.fee_deposit_val"
    1524             :       ",denominations.fee_deposit_frac"
    1525             :       ",dep.wallet_timestamp"
    1526             :       ",dep.exchange_timestamp"
    1527             :       ",dep.refund_deadline"
    1528             :       ",dep.wire_deadline"
    1529             :       ",dep.h_contract_terms"
    1530             :       ",dep.wire_salt"
    1531             :       ",wt.payto_uri AS receiver_wire_account"
    1532             :       " FROM deposits dep"
    1533             :       " JOIN known_coins kc ON (kc.coin_pub = dep.coin_pub)"
    1534             :       " JOIN denominations USING (denominations_serial)"
    1535             :       " JOIN wire_targets wt USING (wire_target_h_payto)"
    1536             :       " WHERE dep.coin_pub=$1"
    1537             :       "   AND dep.merchant_pub=$3"
    1538             :       "   AND dep.h_contract_terms=$2;",
    1539             :       3),
    1540             :     /* Fetch deposits with rowid '\geq' the given parameter */
    1541           0 :     GNUNET_PQ_make_prepare (
    1542             :       "audit_get_deposits_incr",
    1543             :       "SELECT"
    1544             :       " amount_with_fee_val"
    1545             :       ",amount_with_fee_frac"
    1546             :       ",wallet_timestamp"
    1547             :       ",exchange_timestamp"
    1548             :       ",merchant_pub"
    1549             :       ",denom.denom_pub"
    1550             :       ",kc.coin_pub"
    1551             :       ",kc.age_commitment_hash"
    1552             :       ",coin_sig"
    1553             :       ",refund_deadline"
    1554             :       ",wire_deadline"
    1555             :       ",h_contract_terms"
    1556             :       ",wire_salt"
    1557             :       ",payto_uri AS receiver_wire_account"
    1558             :       ",done"
    1559             :       ",deposit_serial_id"
    1560             :       " FROM deposits"
    1561             :       "    JOIN wire_targets USING (wire_target_h_payto)"
    1562             :       "    JOIN known_coins kc USING (coin_pub)"
    1563             :       "    JOIN denominations denom USING (denominations_serial)"
    1564             :       " WHERE ("
    1565             :       "  (deposit_serial_id>=$1)"
    1566             :       " )"
    1567             :       " ORDER BY deposit_serial_id ASC;",
    1568             :       1),
    1569             :     /* Fetch purse deposits with rowid '\geq' the given parameter */
    1570           0 :     GNUNET_PQ_make_prepare (
    1571             :       "audit_get_purse_deposits_incr",
    1572             :       "SELECT"
    1573             :       " pd.amount_with_fee_val"
    1574             :       ",pd.amount_with_fee_frac"
    1575             :       ",pr.amount_with_fee_val AS total_val"
    1576             :       ",pr.amount_with_fee_frac AS total_frac"
    1577             :       ",pr.balance_val"
    1578             :       ",pr.balance_frac"
    1579             :       ",pr.flags"
    1580             :       ",pd.purse_pub"
    1581             :       ",pd.coin_sig"
    1582             :       ",partner_base_url"
    1583             :       ",denom.denom_pub"
    1584             :       ",pm.reserve_pub"
    1585             :       ",kc.coin_pub"
    1586             :       ",kc.age_commitment_hash"
    1587             :       ",pd.purse_deposit_serial_id"
    1588             :       " FROM purse_deposits pd"
    1589             :       " LEFT JOIN partners USING (partner_serial_id)"
    1590             :       " LEFT JOIN purse_merges pm USING (purse_pub)"
    1591             :       " JOIN purse_requests pr USING (purse_pub)"
    1592             :       " JOIN known_coins kc USING (coin_pub)"
    1593             :       " JOIN denominations denom USING (denominations_serial)"
    1594             :       " WHERE ("
    1595             :       "  (purse_deposit_serial_id>=$1)"
    1596             :       " )"
    1597             :       " ORDER BY purse_deposit_serial_id ASC;",
    1598             :       1),
    1599             : 
    1600           0 :     GNUNET_PQ_make_prepare (
    1601             :       "audit_get_account_merge_incr",
    1602             :       "SELECT"
    1603             :       " am.account_merge_request_serial_id"
    1604             :       ",am.reserve_pub"
    1605             :       ",am.purse_pub"
    1606             :       ",pr.h_contract_terms"
    1607             :       ",pr.purse_expiration"
    1608             :       ",pr.amount_with_fee_val"
    1609             :       ",pr.amount_with_fee_frac"
    1610             :       ",pr.age_limit"
    1611             :       ",pr.flags"
    1612             :       ",pr.purse_fee_val"
    1613             :       ",pr.purse_fee_frac"
    1614             :       ",pm.merge_timestamp"
    1615             :       ",am.reserve_sig"
    1616             :       " FROM account_merges am"
    1617             :       " JOIN purse_requests pr USING (purse_pub)"
    1618             :       " JOIN purse_merges pm USING (purse_pub)"
    1619             :       " WHERE ("
    1620             :       "  (account_merge_request_serial_id>=$1)"
    1621             :       " )"
    1622             :       " ORDER BY account_merge_request_serial_id ASC;",
    1623             :       1),
    1624             : 
    1625           0 :     GNUNET_PQ_make_prepare (
    1626             :       "audit_get_purse_merge_incr",
    1627             :       "SELECT"
    1628             :       " pm.purse_merge_request_serial_id"
    1629             :       ",partner_base_url"
    1630             :       ",pr.amount_with_fee_val"
    1631             :       ",pr.amount_with_fee_frac"
    1632             :       ",pr.balance_val"
    1633             :       ",pr.balance_frac"
    1634             :       ",pr.flags"
    1635             :       ",pr.merge_pub"
    1636             :       ",pm.reserve_pub"
    1637             :       ",pm.merge_sig"
    1638             :       ",pm.purse_pub"
    1639             :       ",pm.merge_timestamp"
    1640             :       " FROM purse_merges pm"
    1641             :       " JOIN purse_requests pr USING (purse_pub)"
    1642             :       " LEFT JOIN partners USING (partner_serial_id)"
    1643             :       " WHERE ("
    1644             :       "  (purse_merge_request_serial_id>=$1)"
    1645             :       " )"
    1646             :       " ORDER BY purse_merge_request_serial_id ASC;",
    1647             :       1),
    1648             : 
    1649           0 :     GNUNET_PQ_make_prepare (
    1650             :       "audit_get_history_requests_incr",
    1651             :       "SELECT"
    1652             :       " history_request_serial_id"
    1653             :       ",history_fee_val"
    1654             :       ",history_fee_frac"
    1655             :       ",request_timestamp"
    1656             :       ",reserve_pub"
    1657             :       ",reserve_sig"
    1658             :       " FROM history_requests"
    1659             :       " WHERE ("
    1660             :       "  (history_request_serial_id>=$1)"
    1661             :       " )"
    1662             :       " ORDER BY history_request_serial_id ASC;",
    1663             :       1),
    1664             : 
    1665             : 
    1666           0 :     GNUNET_PQ_make_prepare (
    1667             :       "audit_get_purse_deposits_by_purse",
    1668             :       "SELECT"
    1669             :       " pd.purse_deposit_serial_id"
    1670             :       ",pd.amount_with_fee_val"
    1671             :       ",pd.amount_with_fee_frac"
    1672             :       ",pd.coin_pub"
    1673             :       ",denom.denom_pub"
    1674             :       " FROM purse_deposits pd"
    1675             :       " JOIN known_coins kc USING (coin_pub)"
    1676             :       " JOIN denominations denom USING (denominations_serial)"
    1677             :       " WHERE purse_pub=$1;",
    1678             :       1),
    1679           0 :     GNUNET_PQ_make_prepare (
    1680             :       "audit_get_purse_refunds_incr",
    1681             :       "SELECT"
    1682             :       " purse_pub"
    1683             :       ",purse_refunds_serial_id"
    1684             :       " FROM purse_refunds"
    1685             :       " WHERE ("
    1686             :       "  (purse_refunds_serial_id>=$1)"
    1687             :       " )"
    1688             :       " ORDER BY purse_refunds_serial_id ASC;",
    1689             :       1),
    1690             :     /* Fetch an existing deposit request.
    1691             :        Used in #postgres_lookup_transfer_by_deposit(). */
    1692           0 :     GNUNET_PQ_make_prepare (
    1693             :       "get_deposit_without_wtid",
    1694             :       "SELECT"
    1695             :       " agt.legitimization_requirement_serial_id"
    1696             :       ",dep.wire_salt"
    1697             :       ",wt.payto_uri"
    1698             :       ",dep.amount_with_fee_val"
    1699             :       ",dep.amount_with_fee_frac"
    1700             :       ",denom.fee_deposit_val"
    1701             :       ",denom.fee_deposit_frac"
    1702             :       ",dep.wire_deadline"
    1703             :       " FROM deposits dep"
    1704             :       " JOIN wire_targets wt"
    1705             :       "   USING (wire_target_h_payto)"
    1706             :       " JOIN known_coins kc"
    1707             :       "   ON (kc.coin_pub = dep.coin_pub)"
    1708             :       " JOIN denominations denom"
    1709             :       "   USING (denominations_serial)"
    1710             :       " LEFT JOIN aggregation_transient agt "
    1711             :       "   ON ( (dep.wire_target_h_payto = agt.wire_target_h_payto) AND"
    1712             :       "        (dep.merchant_pub = agt.merchant_pub) )"
    1713             :       " WHERE dep.coin_pub=$1"
    1714             :       "   AND dep.merchant_pub=$3"
    1715             :       "   AND dep.h_contract_terms=$2"
    1716             :       " LIMIT 1;",
    1717             :       3),
    1718             :     /* Used in #postgres_get_ready_deposit() */
    1719           0 :     GNUNET_PQ_make_prepare (
    1720             :       "deposits_get_ready",
    1721             :       "SELECT"
    1722             :       " payto_uri"
    1723             :       ",merchant_pub"
    1724             :       " FROM deposits_by_ready dbr"
    1725             :       "  JOIN deposits dep"
    1726             :       "    ON (dbr.coin_pub = dep.coin_pub AND"
    1727             :       "        dbr.deposit_serial_id = dep.deposit_serial_id)"
    1728             :       "  JOIN wire_targets wt"
    1729             :       "    USING (wire_target_h_payto)"
    1730             :       " WHERE dbr.wire_deadline<=$1"
    1731             :       "   AND dbr.shard >= $2"
    1732             :       "   AND dbr.shard <= $3"
    1733             :       " ORDER BY "
    1734             :       "   dbr.wire_deadline ASC"
    1735             :       "  ,dbr.shard ASC"
    1736             :       " LIMIT 1;",
    1737             :       3),
    1738             :     /* Used in #postgres_aggregate() */
    1739           0 :     GNUNET_PQ_make_prepare (
    1740             :       "aggregate",
    1741             :       "WITH rdy AS (" /* find deposits ready by merchant */
    1742             :       "  SELECT"
    1743             :       "    coin_pub"
    1744             :       "    FROM deposits_for_matching"
    1745             :       "    WHERE refund_deadline<$1" /* filter by shard, only actually executable deposits */
    1746             :       "      AND merchant_pub=$2" /* filter by target merchant */
    1747             :       "    ORDER BY refund_deadline ASC" /* ordering is not critical */
    1748             :       "    LIMIT "
    1749             :       TALER_QUOTE (TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT) /* limits transaction size */
    1750             :       " )"
    1751             :       " ,dep AS (" /* restrict to our merchant and account and mark as done */
    1752             :       "  UPDATE deposits"
    1753             :       "     SET done=TRUE"
    1754             :       "   WHERE coin_pub IN (SELECT coin_pub FROM rdy)"
    1755             :       "     AND merchant_pub=$2" /* theoretically, same coin could be spent at another merchant */
    1756             :       "     AND wire_target_h_payto=$3" /* merchant could have a 2nd bank account */
    1757             :       "     AND done=FALSE" /* theoretically, same coin could be spend at the same merchant a 2nd time */
    1758             :       "   RETURNING"
    1759             :       "     deposit_serial_id"
    1760             :       "    ,coin_pub"
    1761             :       "    ,amount_with_fee_val AS amount_val"
    1762             :       "    ,amount_with_fee_frac AS amount_frac)"
    1763             :       " ,ref AS (" /* find applicable refunds -- NOTE: may do a full join on the master, maybe find a left-join way to integrate with query above to push it to the shards? */
    1764             :       "  SELECT"
    1765             :       "    amount_with_fee_val AS refund_val"
    1766             :       "   ,amount_with_fee_frac AS refund_frac"
    1767             :       "   ,coin_pub"
    1768             :       "   ,deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */
    1769             :       "    FROM refunds"
    1770             :       "   WHERE coin_pub IN (SELECT coin_pub FROM dep)"
    1771             :       "     AND deposit_serial_id IN (SELECT deposit_serial_id FROM dep))"
    1772             :       " ,ref_by_coin AS (" /* total up refunds by coin */
    1773             :       "  SELECT"
    1774             :       "    SUM(refund_val) AS sum_refund_val"
    1775             :       "   ,SUM(refund_frac) AS sum_refund_frac"
    1776             :       "   ,coin_pub"
    1777             :       "   ,deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */
    1778             :       "    FROM ref"
    1779             :       "   GROUP BY coin_pub, deposit_serial_id)"
    1780             :       " ,norm_ref_by_coin AS (" /* normalize */
    1781             :       "  SELECT"
    1782             :       "    sum_refund_val + sum_refund_frac / 100000000 AS norm_refund_val"
    1783             :       "   ,sum_refund_frac % 100000000 AS norm_refund_frac"
    1784             :       "   ,coin_pub"
    1785             :       "   ,deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */
    1786             :       "    FROM ref_by_coin)"
    1787             :       " ,fully_refunded_coins AS (" /* find applicable refunds -- NOTE: may do a full join on the master, maybe find a left-join way to integrate with query above to push it to the shards? */
    1788             :       "  SELECT"
    1789             :       "    dep.coin_pub"
    1790             :       "    FROM norm_ref_by_coin norm"
    1791             :       "    JOIN dep"
    1792             :       "      ON (norm.coin_pub = dep.coin_pub"
    1793             :       "      AND norm.deposit_serial_id = dep.deposit_Serial_id"
    1794             :       "      AND norm.norm_refund_val = dep.amount_val"
    1795             :       "      AND norm.norm_refund_frac = dep.amount_frac))"
    1796             :       " ,fees AS (" /* find deposit fees for not fully refunded deposits */
    1797             :       "  SELECT"
    1798             :       "    denom.fee_deposit_val AS fee_val"
    1799             :       "   ,denom.fee_deposit_frac AS fee_frac"
    1800             :       "   ,cs.deposit_serial_id" /* ensures we get the fee for each coin, not once per denomination */
    1801             :       "    FROM dep cs"
    1802             :       "    JOIN known_coins kc" /* NOTE: may do a full join on the master, maybe find a left-join way to integrate with query above to push it to the shards? */
    1803             :       "      USING (coin_pub)"
    1804             :       "    JOIN denominations denom"
    1805             :       "      USING (denominations_serial)"
    1806             :       "    WHERE coin_pub NOT IN (SELECT coin_pub FROM fully_refunded_coins))"
    1807             :       " ,dummy AS (" /* add deposits to aggregation_tracking */
    1808             :       "    INSERT INTO aggregation_tracking"
    1809             :       "    (deposit_serial_id"
    1810             :       "    ,wtid_raw)"
    1811             :       "    SELECT deposit_serial_id,$4"
    1812             :       "      FROM dep)"
    1813             :       "SELECT" /* calculate totals (deposits, refunds and fees) */
    1814             :       "  CAST(COALESCE(SUM(dep.amount_val),0) AS INT8) AS sum_deposit_value" /* cast needed, otherwise we get NUMBER */
    1815             :       " ,COALESCE(SUM(dep.amount_frac),0) AS sum_deposit_fraction" /* SUM over INT returns INT8 */
    1816             :       " ,CAST(COALESCE(SUM(ref.refund_val),0) AS INT8) AS sum_refund_value"
    1817             :       " ,COALESCE(SUM(ref.refund_frac),0) AS sum_refund_fraction"
    1818             :       " ,CAST(COALESCE(SUM(fees.fee_val),0) AS INT8) AS sum_fee_value"
    1819             :       " ,COALESCE(SUM(fees.fee_frac),0) AS sum_fee_fraction"
    1820             :       " FROM dep "
    1821             :       "   FULL OUTER JOIN ref ON (FALSE)"    /* We just want all sums */
    1822             :       "   FULL OUTER JOIN fees ON (FALSE);",
    1823             :       4),
    1824             : 
    1825             : 
    1826             :     /* Used in #postgres_create_aggregation_transient() */
    1827           0 :     GNUNET_PQ_make_prepare (
    1828             :       "create_aggregation_transient",
    1829             :       "INSERT INTO aggregation_transient"
    1830             :       " (amount_val"
    1831             :       " ,amount_frac"
    1832             :       " ,merchant_pub"
    1833             :       " ,wire_target_h_payto"
    1834             :       " ,legitimization_requirement_serial_id"
    1835             :       " ,exchange_account_section"
    1836             :       " ,wtid_raw)"
    1837             :       " VALUES ($1, $2, $3, $4, $5, $6, $7);",
    1838             :       7),
    1839             :     /* Used in #postgres_select_aggregation_transient() */
    1840           0 :     GNUNET_PQ_make_prepare (
    1841             :       "select_aggregation_transient",
    1842             :       "SELECT"
    1843             :       "  amount_val"
    1844             :       " ,amount_frac"
    1845             :       " ,wtid_raw"
    1846             :       " FROM aggregation_transient"
    1847             :       " WHERE wire_target_h_payto=$1"
    1848             :       "   AND merchant_pub=$2"
    1849             :       "   AND exchange_account_section=$3;",
    1850             :       3),
    1851             :     /* Used in #postgres_find_aggregation_transient() */
    1852           0 :     GNUNET_PQ_make_prepare (
    1853             :       "find_transient_aggregations",
    1854             :       "SELECT"
    1855             :       "  amount_val"
    1856             :       " ,amount_frac"
    1857             :       " ,wtid_raw"
    1858             :       " ,merchant_pub"
    1859             :       " ,payto_uri"
    1860             :       " FROM aggregation_transient atr"
    1861             :       " JOIN wire_targets wt USING (wire_target_h_payto)"
    1862             :       " WHERE atr.wire_target_h_payto=$1;",
    1863             :       1),
    1864             :     /* Used in #postgres_update_aggregation_transient() */
    1865           0 :     GNUNET_PQ_make_prepare (
    1866             :       "update_aggregation_transient",
    1867             :       "UPDATE aggregation_transient"
    1868             :       " SET amount_val=$1"
    1869             :       "    ,amount_frac=$2"
    1870             :       "    ,legitimization_requirement_serial_id=$5"
    1871             :       " WHERE wire_target_h_payto=$3"
    1872             :       "   AND wtid_raw=$4",
    1873             :       5),
    1874             :     /* Used in #postgres_delete_aggregation_transient() */
    1875           0 :     GNUNET_PQ_make_prepare (
    1876             :       "delete_aggregation_transient",
    1877             :       "DELETE FROM aggregation_transient"
    1878             :       " WHERE wire_target_h_payto=$1"
    1879             :       "   AND wtid_raw=$2",
    1880             :       2),
    1881             : 
    1882             :     /* Used in #postgres_get_coin_transactions() to obtain information
    1883             :        about how a coin has been spend with /deposit requests. */
    1884           0 :     GNUNET_PQ_make_prepare (
    1885             :       "get_deposit_with_coin_pub",
    1886             :       "SELECT"
    1887             :       " dep.amount_with_fee_val"
    1888             :       ",dep.amount_with_fee_frac"
    1889             :       ",denoms.fee_deposit_val"
    1890             :       ",denoms.fee_deposit_frac"
    1891             :       ",denoms.denom_pub_hash"
    1892             :       ",kc.age_commitment_hash"
    1893             :       ",dep.wallet_timestamp"
    1894             :       ",dep.refund_deadline"
    1895             :       ",dep.wire_deadline"
    1896             :       ",dep.merchant_pub"
    1897             :       ",dep.h_contract_terms"
    1898             :       ",dep.wire_salt"
    1899             :       ",wt.payto_uri"
    1900             :       ",dep.coin_sig"
    1901             :       ",dep.deposit_serial_id"
    1902             :       ",dep.done"
    1903             :       " FROM deposits dep"
    1904             :       "    JOIN wire_targets wt"
    1905             :       "      USING (wire_target_h_payto)"
    1906             :       "    JOIN known_coins kc"
    1907             :       "      ON (kc.coin_pub = dep.coin_pub)"
    1908             :       "    JOIN denominations denoms"
    1909             :       "      USING (denominations_serial)"
    1910             :       " WHERE dep.coin_pub=$1;",
    1911             :       1),
    1912             : 
    1913             :     /* Used in #postgres_get_link_data(). */
    1914           0 :     GNUNET_PQ_make_prepare (
    1915             :       "get_link",
    1916             :       "SELECT "
    1917             :       " tp.transfer_pub"
    1918             :       ",denoms.denom_pub"
    1919             :       ",rrc.ev_sig"
    1920             :       ",rrc.ewv"
    1921             :       ",rrc.link_sig"
    1922             :       ",rrc.freshcoin_index"
    1923             :       ",rrc.coin_ev"
    1924             :       " FROM refresh_commitments"
    1925             :       "     JOIN refresh_revealed_coins rrc"
    1926             :       "       USING (melt_serial_id)"
    1927             :       "     JOIN refresh_transfer_keys tp"
    1928             :       "       USING (melt_serial_id)"
    1929             :       "     JOIN denominations denoms"
    1930             :       "       ON (rrc.denominations_serial = denoms.denominations_serial)"
    1931             :       " WHERE old_coin_pub=$1"
    1932             :       " ORDER BY tp.transfer_pub, rrc.freshcoin_index ASC",
    1933             :       1),
    1934             :     /* Used in #postgres_lookup_wire_transfer */
    1935           0 :     GNUNET_PQ_make_prepare (
    1936             :       "lookup_transactions",
    1937             :       "SELECT"
    1938             :       " aggregation_serial_id"
    1939             :       ",deposits.h_contract_terms"
    1940             :       ",payto_uri"
    1941             :       ",wire_targets.wire_target_h_payto"
    1942             :       ",kc.coin_pub"
    1943             :       ",deposits.merchant_pub"
    1944             :       ",wire_out.execution_date"
    1945             :       ",deposits.amount_with_fee_val"
    1946             :       ",deposits.amount_with_fee_frac"
    1947             :       ",denom.fee_deposit_val"
    1948             :       ",denom.fee_deposit_frac"
    1949             :       ",denom.denom_pub"
    1950             :       " FROM aggregation_tracking"
    1951             :       "    JOIN deposits"
    1952             :       "      USING (deposit_serial_id)"
    1953             :       "    JOIN wire_targets"
    1954             :       "      USING (wire_target_h_payto)"
    1955             :       "    JOIN known_coins kc"
    1956             :       "      USING (coin_pub)"
    1957             :       "    JOIN denominations denom"
    1958             :       "      USING (denominations_serial)"
    1959             :       "    JOIN wire_out"
    1960             :       "      USING (wtid_raw)"
    1961             :       " WHERE wtid_raw=$1;",
    1962             :       1),
    1963             :     /* Used in #postgres_lookup_transfer_by_deposit */
    1964           0 :     GNUNET_PQ_make_prepare (
    1965             :       "lookup_deposit_wtid",
    1966             :       "SELECT"
    1967             :       " aggregation_tracking.wtid_raw"
    1968             :       ",wire_out.execution_date"
    1969             :       ",dep.amount_with_fee_val"
    1970             :       ",dep.amount_with_fee_frac"
    1971             :       ",dep.wire_salt"
    1972             :       ",wt.payto_uri"
    1973             :       ",denom.fee_deposit_val"
    1974             :       ",denom.fee_deposit_frac"
    1975             :       " FROM deposits dep"
    1976             :       "    JOIN wire_targets wt"
    1977             :       "      USING (wire_target_h_payto)"
    1978             :       "    JOIN aggregation_tracking"
    1979             :       "      USING (deposit_serial_id)"
    1980             :       "    JOIN known_coins kc"
    1981             :       "      ON (kc.coin_pub = dep.coin_pub)"
    1982             :       "    JOIN denominations denom"
    1983             :       "      USING (denominations_serial)"
    1984             :       "    JOIN wire_out"
    1985             :       "      USING (wtid_raw)"
    1986             :       " WHERE dep.coin_pub=$1"
    1987             :       "   AND dep.merchant_pub=$3"
    1988             :       "   AND dep.h_contract_terms=$2",
    1989             :       3),
    1990             :     /* Used in #postgres_insert_aggregation_tracking */
    1991           0 :     GNUNET_PQ_make_prepare (
    1992             :       "insert_aggregation_tracking",
    1993             :       "INSERT INTO aggregation_tracking "
    1994             :       "(deposit_serial_id"
    1995             :       ",wtid_raw"
    1996             :       ") VALUES "
    1997             :       "($1, $2);",
    1998             :       2),
    1999             :     /* Used in #postgres_get_wire_fee() */
    2000           0 :     GNUNET_PQ_make_prepare (
    2001             :       "get_wire_fee",
    2002             :       "SELECT "
    2003             :       " start_date"
    2004             :       ",end_date"
    2005             :       ",wire_fee_val"
    2006             :       ",wire_fee_frac"
    2007             :       ",closing_fee_val"
    2008             :       ",closing_fee_frac"
    2009             :       ",wad_fee_val"
    2010             :       ",wad_fee_frac"
    2011             :       ",master_sig"
    2012             :       " FROM wire_fee"
    2013             :       " WHERE wire_method=$1"
    2014             :       "   AND start_date <= $2"
    2015             :       "   AND end_date > $2;",
    2016             :       2),
    2017             :     /* Used in #postgres_get_global_fee() */
    2018           0 :     GNUNET_PQ_make_prepare (
    2019             :       "get_global_fee",
    2020             :       "SELECT "
    2021             :       " start_date"
    2022             :       ",end_date"
    2023             :       ",history_fee_val"
    2024             :       ",history_fee_frac"
    2025             :       ",kyc_fee_val"
    2026             :       ",kyc_fee_frac"
    2027             :       ",account_fee_val"
    2028             :       ",account_fee_frac"
    2029             :       ",purse_fee_val"
    2030             :       ",purse_fee_frac"
    2031             :       ",purse_timeout"
    2032             :       ",kyc_timeout"
    2033             :       ",history_expiration"
    2034             :       ",purse_account_limit"
    2035             :       ",master_sig"
    2036             :       " FROM global_fee"
    2037             :       " WHERE start_date <= $1"
    2038             :       "   AND end_date > $1;",
    2039             :       1),
    2040             :     /* Used in #postgres_get_global_fees() */
    2041           0 :     GNUNET_PQ_make_prepare (
    2042             :       "get_global_fees",
    2043             :       "SELECT "
    2044             :       " start_date"
    2045             :       ",end_date"
    2046             :       ",history_fee_val"
    2047             :       ",history_fee_frac"
    2048             :       ",kyc_fee_val"
    2049             :       ",kyc_fee_frac"
    2050             :       ",account_fee_val"
    2051             :       ",account_fee_frac"
    2052             :       ",purse_fee_val"
    2053             :       ",purse_fee_frac"
    2054             :       ",purse_timeout"
    2055             :       ",kyc_timeout"
    2056             :       ",history_expiration"
    2057             :       ",purse_account_limit"
    2058             :       ",master_sig"
    2059             :       " FROM global_fee"
    2060             :       " WHERE start_date >= $1",
    2061             :       1),
    2062             :     /* Used in #postgres_insert_wire_fee */
    2063           0 :     GNUNET_PQ_make_prepare (
    2064             :       "insert_wire_fee",
    2065             :       "INSERT INTO wire_fee "
    2066             :       "(wire_method"
    2067             :       ",start_date"
    2068             :       ",end_date"
    2069             :       ",wire_fee_val"
    2070             :       ",wire_fee_frac"
    2071             :       ",closing_fee_val"
    2072             :       ",closing_fee_frac"
    2073             :       ",wad_fee_val"
    2074             :       ",wad_fee_frac"
    2075             :       ",master_sig"
    2076             :       ") VALUES "
    2077             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);",
    2078             :       10),
    2079             :     /* Used in #postgres_insert_global_fee */
    2080           0 :     GNUNET_PQ_make_prepare (
    2081             :       "insert_global_fee",
    2082             :       "INSERT INTO global_fee "
    2083             :       "(start_date"
    2084             :       ",end_date"
    2085             :       ",history_fee_val"
    2086             :       ",history_fee_frac"
    2087             :       ",kyc_fee_val"
    2088             :       ",kyc_fee_frac"
    2089             :       ",account_fee_val"
    2090             :       ",account_fee_frac"
    2091             :       ",purse_fee_val"
    2092             :       ",purse_fee_frac"
    2093             :       ",purse_timeout"
    2094             :       ",kyc_timeout"
    2095             :       ",history_expiration"
    2096             :       ",purse_account_limit"
    2097             :       ",master_sig"
    2098             :       ") VALUES "
    2099             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15);",
    2100             :       15),
    2101             :     /* Used in #postgres_store_wire_transfer_out */
    2102           0 :     GNUNET_PQ_make_prepare (
    2103             :       "insert_wire_out",
    2104             :       "INSERT INTO wire_out "
    2105             :       "(execution_date"
    2106             :       ",wtid_raw"
    2107             :       ",wire_target_h_payto"
    2108             :       ",exchange_account_section"
    2109             :       ",amount_val"
    2110             :       ",amount_frac"
    2111             :       ") VALUES "
    2112             :       "($1, $2, $3, $4, $5, $6);",
    2113             :       6),
    2114           0 :     GNUNET_PQ_make_prepare (
    2115             :       "insert_into_table_wire_out",
    2116             :       "INSERT INTO wire_out"
    2117             :       "(wireout_uuid"
    2118             :       ",execution_date"
    2119             :       ",wtid_raw"
    2120             :       ",wire_target_h_payto"
    2121             :       ",exchange_account_section"
    2122             :       ",amount_val"
    2123             :       ",amount_frac"
    2124             :       ") VALUES "
    2125             :       "($1, $2, $3, $4, $5, $6, $7);",
    2126             :       7),
    2127             :     /* Used in #postgres_wire_prepare_data_insert() to store
    2128             :        wire transfer information before actually committing it with the bank */
    2129           0 :     GNUNET_PQ_make_prepare (
    2130             :       "wire_prepare_data_insert",
    2131             :       "INSERT INTO prewire "
    2132             :       "(wire_method"
    2133             :       ",buf"
    2134             :       ") VALUES "
    2135             :       "($1, $2);",
    2136             :       2),
    2137             :     /* Used in #postgres_wire_prepare_data_mark_finished() */
    2138           0 :     GNUNET_PQ_make_prepare (
    2139             :       "wire_prepare_data_mark_done",
    2140             :       "UPDATE prewire"
    2141             :       " SET finished=TRUE"
    2142             :       " WHERE prewire_uuid=$1;",
    2143             :       1),
    2144             :     /* Used in #postgres_wire_prepare_data_mark_failed() */
    2145           0 :     GNUNET_PQ_make_prepare (
    2146             :       "wire_prepare_data_mark_failed",
    2147             :       "UPDATE prewire"
    2148             :       " SET failed=TRUE"
    2149             :       " WHERE prewire_uuid=$1;",
    2150             :       1),
    2151             :     /* Used in #postgres_wire_prepare_data_get() */
    2152           0 :     GNUNET_PQ_make_prepare (
    2153             :       "wire_prepare_data_get",
    2154             :       "SELECT"
    2155             :       " prewire_uuid"
    2156             :       ",wire_method"
    2157             :       ",buf"
    2158             :       " FROM prewire"
    2159             :       " WHERE prewire_uuid >= $1"
    2160             :       "   AND finished=FALSE"
    2161             :       "   AND failed=FALSE"
    2162             :       " ORDER BY prewire_uuid ASC"
    2163             :       " LIMIT $2;",
    2164             :       2),
    2165             :     /* Used in #postgres_select_deposits_missing_wire */
    2166             :     // FIXME: used by the auditor; can probably be done
    2167             :     // smarter by checking if 'done' or 'blocked'
    2168             :     // are set correctly when going over deposits, instead
    2169             :     // of JOINing with refunds.
    2170           0 :     GNUNET_PQ_make_prepare (
    2171             :       "deposits_get_overdue",
    2172             :       "SELECT"
    2173             :       " deposit_serial_id"
    2174             :       ",coin_pub"
    2175             :       ",amount_with_fee_val"
    2176             :       ",amount_with_fee_frac"
    2177             :       ",payto_uri"
    2178             :       ",wire_deadline"
    2179             :       ",done"
    2180             :       " FROM deposits d"
    2181             :       "   JOIN known_coins"
    2182             :       "     USING (coin_pub)"
    2183             :       "   JOIN wire_targets"
    2184             :       "     USING (wire_target_h_payto)"
    2185             :       " WHERE wire_deadline >= $1"
    2186             :       " AND wire_deadline < $2"
    2187             :       " AND NOT (EXISTS (SELECT 1"
    2188             :       "            FROM refunds r"
    2189             :       "            WHERE (r.coin_pub = d.coin_pub) AND (r.deposit_serial_id = d.deposit_serial_id))"
    2190             :       "       OR EXISTS (SELECT 1"
    2191             :       "            FROM aggregation_tracking"
    2192             :       "            WHERE (aggregation_tracking.deposit_serial_id = d.deposit_serial_id)))"
    2193             :       " ORDER BY wire_deadline ASC",
    2194             :       2),
    2195             :     /* Used in #postgres_select_wire_out_above_serial_id() */
    2196           0 :     GNUNET_PQ_make_prepare (
    2197             :       "audit_get_wire_incr",
    2198             :       "SELECT"
    2199             :       " wireout_uuid"
    2200             :       ",execution_date"
    2201             :       ",wtid_raw"
    2202             :       ",payto_uri"
    2203             :       ",amount_val"
    2204             :       ",amount_frac"
    2205             :       " FROM wire_out"
    2206             :       "   JOIN wire_targets"
    2207             :       "     USING (wire_target_h_payto)"
    2208             :       " WHERE wireout_uuid>=$1"
    2209             :       " ORDER BY wireout_uuid ASC;",
    2210             :       1),
    2211             :     /* Used in #postgres_select_wire_out_above_serial_id_by_account() */
    2212           0 :     GNUNET_PQ_make_prepare (
    2213             :       "audit_get_wire_incr_by_account",
    2214             :       "SELECT"
    2215             :       " wireout_uuid"
    2216             :       ",execution_date"
    2217             :       ",wtid_raw"
    2218             :       ",payto_uri"
    2219             :       ",amount_val"
    2220             :       ",amount_frac"
    2221             :       " FROM wire_out"
    2222             :       "   JOIN wire_targets"
    2223             :       "     USING (wire_target_h_payto)"
    2224             :       " WHERE "
    2225             :       "      wireout_uuid>=$1 "
    2226             :       "  AND exchange_account_section=$2"
    2227             :       " ORDER BY wireout_uuid ASC;",
    2228             :       2),
    2229             :     /* Used in #postgres_select_recoup_above_serial_id() to obtain recoup transactions */
    2230           0 :     GNUNET_PQ_make_prepare (
    2231             :       "recoup_get_incr",
    2232             :       "SELECT"
    2233             :       " recoup_uuid"
    2234             :       ",recoup_timestamp"
    2235             :       ",reserves.reserve_pub"
    2236             :       ",coins.coin_pub"
    2237             :       ",coin_sig"
    2238             :       ",coin_blind"
    2239             :       ",ro.h_blind_ev"
    2240             :       ",denoms.denom_pub_hash"
    2241             :       ",coins.denom_sig"
    2242             :       ",coins.age_commitment_hash"
    2243             :       ",denoms.denom_pub"
    2244             :       ",amount_val"
    2245             :       ",amount_frac"
    2246             :       " FROM recoup"
    2247             :       "    JOIN known_coins coins"
    2248             :       "      USING (coin_pub)"
    2249             :       "    JOIN reserves_out ro"
    2250             :       "      USING (reserve_out_serial_id)"
    2251             :       "    JOIN reserves"
    2252             :       "      USING (reserve_uuid)"
    2253             :       "    JOIN denominations denoms"
    2254             :       "      ON (coins.denominations_serial = denoms.denominations_serial)"
    2255             :       " WHERE recoup_uuid>=$1"
    2256             :       " ORDER BY recoup_uuid ASC;",
    2257             :       1),
    2258             :     /* Used in #postgres_select_recoup_refresh_above_serial_id() to obtain
    2259             :        recoup-refresh transactions */
    2260           0 :     GNUNET_PQ_make_prepare (
    2261             :       "recoup_refresh_get_incr",
    2262             :       "SELECT"
    2263             :       " recoup_refresh_uuid"
    2264             :       ",recoup_timestamp"
    2265             :       ",old_coins.coin_pub AS old_coin_pub"
    2266             :       ",new_coins.age_commitment_hash"
    2267             :       ",old_denoms.denom_pub_hash AS old_denom_pub_hash"
    2268             :       ",new_coins.coin_pub As coin_pub"
    2269             :       ",coin_sig"
    2270             :       ",coin_blind"
    2271             :       ",new_denoms.denom_pub AS denom_pub"
    2272             :       ",rrc.h_coin_ev AS h_blind_ev"
    2273             :       ",new_denoms.denom_pub_hash"
    2274             :       ",new_coins.denom_sig AS denom_sig"
    2275             :       ",amount_val"
    2276             :       ",amount_frac"
    2277             :       " FROM recoup_refresh"
    2278             :       "    INNER JOIN refresh_revealed_coins rrc"
    2279             :       "      USING (rrc_serial)"
    2280             :       "    INNER JOIN refresh_commitments rfc"
    2281             :       "      ON (rrc.melt_serial_id = rfc.melt_serial_id)"
    2282             :       "    INNER JOIN known_coins old_coins"
    2283             :       "      ON (rfc.old_coin_pub = old_coins.coin_pub)"
    2284             :       "    INNER JOIN known_coins new_coins"
    2285             :       "      ON (new_coins.coin_pub = recoup_refresh.coin_pub)"
    2286             :       "    INNER JOIN denominations new_denoms"
    2287             :       "      ON (new_coins.denominations_serial = new_denoms.denominations_serial)"
    2288             :       "    INNER JOIN denominations old_denoms"
    2289             :       "      ON (old_coins.denominations_serial = old_denoms.denominations_serial)"
    2290             :       " WHERE recoup_refresh_uuid>=$1"
    2291             :       " ORDER BY recoup_refresh_uuid ASC;",
    2292             :       1),
    2293             :     /* Used in #postgres_select_reserve_closed_above_serial_id() to
    2294             :        obtain information about closed reserves */
    2295           0 :     GNUNET_PQ_make_prepare (
    2296             :       "reserves_close_get_incr",
    2297             :       "SELECT"
    2298             :       " close_uuid"
    2299             :       ",reserves.reserve_pub"
    2300             :       ",execution_date"
    2301             :       ",wtid"
    2302             :       ",payto_uri AS receiver_account"
    2303             :       ",amount_val"
    2304             :       ",amount_frac"
    2305             :       ",closing_fee_val"
    2306             :       ",closing_fee_frac"
    2307             :       " FROM reserves_close"
    2308             :       "   JOIN wire_targets"
    2309             :       "     USING (wire_target_h_payto)"
    2310             :       "   JOIN reserves"
    2311             :       "     USING (reserve_pub)"
    2312             :       " WHERE close_uuid>=$1"
    2313             :       " ORDER BY close_uuid ASC;",
    2314             :       1),
    2315             :     /* Used in #postgres_get_reserve_history() to obtain recoup transactions
    2316             :        for a reserve - query optimization should be disabled i.e.
    2317             :        BEGIN; SET LOCAL join_collapse_limit=1; query; COMMIT; */
    2318           0 :     GNUNET_PQ_make_prepare (
    2319             :       "recoup_by_reserve",
    2320             :       /*
    2321             :       "SELECT"
    2322             :       " recoup.coin_pub"
    2323             :       ",recoup.coin_sig"
    2324             :       ",recoup.coin_blind"
    2325             :       ",recoup.amount_val"
    2326             :       ",recoup.amount_frac"
    2327             :       ",recoup.recoup_timestamp"
    2328             :       ",denominations.denom_pub_hash"
    2329             :       ",known_coins.denom_sig"
    2330             :       " FROM denominations"
    2331             :       " JOIN (known_coins"
    2332             :       "   JOIN recoup "
    2333             :       "   ON (recoup.coin_pub = known_coins.coin_pub))"
    2334             :       "  ON (known_coins.denominations_serial = denominations.denominations_serial)"
    2335             :       " WHERE recoup.coin_pub"
    2336             :       " IN (SELECT coin_pub"
    2337             :       "     FROM recoup_by_reserve"
    2338             :       "     JOIN (reserves_out"
    2339             :       "       JOIN (reserves_out_by_reserve"
    2340             :       "         JOIN reserves"
    2341             :       "           ON (reserves.reserve_uuid = reserves_out_by_reserve.reserve_uuid))"
    2342             :       "       ON (reserves_out_by_reserve.h_blind_ev = reserves_out.h_blind_ev))"
    2343             :       "     ON (recoup_by_reserve.reserve_out_serial_id = reserves_out.reserve_out_serial_id)"
    2344             :       "     WHERE reserves.reserve_pub=$1);",
    2345             :       */
    2346             :       "SELECT robr.coin_pub "
    2347             :       "  ,robr.coin_sig "
    2348             :       "  ,robr.coin_blind "
    2349             :       "  ,robr.amount_val "
    2350             :       "  ,robr.amount_frac "
    2351             :       "  ,robr.recoup_timestamp "
    2352             :       "  ,denominations.denom_pub_hash "
    2353             :       "  ,robr.denom_sig "
    2354             :       "FROM denominations "
    2355             :       "  JOIN exchange_do_recoup_by_reserve($1) robr"
    2356             :       " USING (denominations_serial);",
    2357             :       1),
    2358             :     /* Used in #postgres_get_reserve_status() to obtain recoup transactions
    2359             :        for a reserve - query optimization should be disabled i.e.
    2360             :        BEGIN; SET LOCAL join_collapse_limit=1; query; COMMIT; */
    2361           0 :     GNUNET_PQ_make_prepare (
    2362             :       "recoup_by_reserve_truncated",
    2363             :       /*
    2364             :       "SELECT"
    2365             :       " recoup.coin_pub"
    2366             :       ",recoup.coin_sig"
    2367             :       ",recoup.coin_blind"
    2368             :       ",recoup.amount_val"
    2369             :       ",recoup.amount_frac"
    2370             :       ",recoup.recoup_timestamp"
    2371             :       ",denominations.denom_pub_hash"
    2372             :       ",known_coins.denom_sig"
    2373             :       " FROM denominations"
    2374             :       " JOIN (known_coins"
    2375             :       "   JOIN recoup "
    2376             :       "   ON (recoup.coin_pub = known_coins.coin_pub))"
    2377             :       "  ON (known_coins.denominations_serial = denominations.denominations_serial)"
    2378             :       " WHERE recoup_timestamp>=$2"
    2379             :       " AND recoup.coin_pub"
    2380             :       "  IN (SELECT coin_pub"
    2381             :       "     FROM recoup_by_reserve"
    2382             :       "     JOIN (reserves_out"
    2383             :       "       JOIN (reserves_out_by_reserve"
    2384             :       "         JOIN reserves"
    2385             :       "           ON (reserves.reserve_uuid = reserves_out_by_reserve.reserve_uuid))"
    2386             :       "       ON (reserves_out_by_reserve.h_blind_ev = reserves_out.h_blind_ev))"
    2387             :       "     ON (recoup_by_reserve.reserve_out_serial_id = reserves_out.reserve_out_serial_id)"
    2388             :       "     WHERE reserves.reserve_pub=$1);",
    2389             :       */
    2390             :       "SELECT robr.coin_pub "
    2391             :       "  ,robr.coin_sig "
    2392             :       "  ,robr.coin_blind "
    2393             :       "  ,robr.amount_val "
    2394             :       "  ,robr.amount_frac "
    2395             :       "  ,robr.recoup_timestamp "
    2396             :       "  ,denominations.denom_pub_hash "
    2397             :       "  ,robr.denom_sig "
    2398             :       "FROM denominations "
    2399             :       "  JOIN exchange_do_recoup_by_reserve($1) robr"
    2400             :       "    USING (denominations_serial)"
    2401             :       " WHERE recoup_timestamp>=$2;",
    2402             :       2),
    2403             :     /* Used in #postgres_get_coin_transactions() to obtain recoup transactions
    2404             :        affecting old coins of refreshed coins */
    2405           0 :     GNUNET_PQ_make_prepare (
    2406             :       "recoup_by_old_coin",
    2407             :       "SELECT"
    2408             :       " coins.coin_pub"
    2409             :       ",coin_sig"
    2410             :       ",coin_blind"
    2411             :       ",amount_val"
    2412             :       ",amount_frac"
    2413             :       ",recoup_timestamp"
    2414             :       ",denoms.denom_pub_hash"
    2415             :       ",coins.denom_sig"
    2416             :       ",recoup_refresh_uuid"
    2417             :       " FROM recoup_refresh"
    2418             :       " JOIN known_coins coins"
    2419             :       "   USING (coin_pub)"
    2420             :       " JOIN denominations denoms"
    2421             :       "   USING (denominations_serial)"
    2422             :       " WHERE rrc_serial IN"
    2423             :       "   (SELECT rrc.rrc_serial"
    2424             :       "    FROM refresh_commitments"
    2425             :       "       JOIN refresh_revealed_coins rrc"
    2426             :       "           USING (melt_serial_id)"
    2427             :       "    WHERE old_coin_pub=$1);",
    2428             :       1),
    2429             :     /* Used in #postgres_get_reserve_history() */
    2430           0 :     GNUNET_PQ_make_prepare (
    2431             :       "close_by_reserve",
    2432             :       "SELECT"
    2433             :       " amount_val"
    2434             :       ",amount_frac"
    2435             :       ",closing_fee_val"
    2436             :       ",closing_fee_frac"
    2437             :       ",execution_date"
    2438             :       ",payto_uri AS receiver_account"
    2439             :       ",wtid"
    2440             :       " FROM reserves_close"
    2441             :       "   JOIN wire_targets"
    2442             :       "     USING (wire_target_h_payto)"
    2443             :       " WHERE reserve_pub=$1;",
    2444             :       1),
    2445             :     /* Used in #postgres_get_reserve_status() */
    2446           0 :     GNUNET_PQ_make_prepare (
    2447             :       "close_by_reserve_truncated",
    2448             :       "SELECT"
    2449             :       " amount_val"
    2450             :       ",amount_frac"
    2451             :       ",closing_fee_val"
    2452             :       ",closing_fee_frac"
    2453             :       ",execution_date"
    2454             :       ",payto_uri AS receiver_account"
    2455             :       ",wtid"
    2456             :       " FROM reserves_close"
    2457             :       "   JOIN wire_targets"
    2458             :       "     USING (wire_target_h_payto)"
    2459             :       " WHERE reserve_pub=$1"
    2460             :       "   AND execution_date>=$2;",
    2461             :       2),
    2462             :     /* Used in #postgres_get_reserve_history() */
    2463           0 :     GNUNET_PQ_make_prepare (
    2464             :       "merge_by_reserve",
    2465             :       "SELECT"
    2466             :       " pr.amount_with_fee_val"
    2467             :       ",pr.amount_with_fee_frac"
    2468             :       ",pr.balance_val"
    2469             :       ",pr.balance_frac"
    2470             :       ",pr.purse_fee_val"
    2471             :       ",pr.purse_fee_frac"
    2472             :       ",pr.h_contract_terms"
    2473             :       ",pr.merge_pub"
    2474             :       ",am.reserve_sig"
    2475             :       ",pm.purse_pub"
    2476             :       ",pm.merge_timestamp"
    2477             :       ",pr.purse_expiration"
    2478             :       ",pr.age_limit"
    2479             :       ",pr.flags"
    2480             :       " FROM purse_merges pm"
    2481             :       "   JOIN purse_requests pr"
    2482             :       "     USING (purse_pub)"
    2483             :       "   JOIN account_merges am"
    2484             :       "     ON (am.purse_pub = pm.purse_pub AND"
    2485             :       "         am.reserve_pub = pm.reserve_pub)"
    2486             :       " WHERE pm.reserve_pub=$1"
    2487             :       "  AND pm.partner_serial_id=0" /* must be local! */
    2488             :       "  AND pr.finished"
    2489             :       "  AND NOT pr.refunded;",
    2490             :       1),
    2491             :     /* Used in #postgres_get_reserve_status() */
    2492           0 :     GNUNET_PQ_make_prepare (
    2493             :       "merge_by_reserve_truncated",
    2494             :       "SELECT"
    2495             :       " pr.amount_with_fee_val"
    2496             :       ",pr.amount_with_fee_frac"
    2497             :       ",pr.balance_val"
    2498             :       ",pr.balance_frac"
    2499             :       ",pr.purse_fee_val"
    2500             :       ",pr.purse_fee_frac"
    2501             :       ",pr.h_contract_terms"
    2502             :       ",pr.merge_pub"
    2503             :       ",am.reserve_sig"
    2504             :       ",pm.purse_pub"
    2505             :       ",pm.merge_timestamp"
    2506             :       ",pr.purse_expiration"
    2507             :       ",pr.age_limit"
    2508             :       ",pr.flags"
    2509             :       " FROM purse_merges pm"
    2510             :       "   JOIN purse_requests pr"
    2511             :       "     USING (purse_pub)"
    2512             :       "   JOIN account_merges am"
    2513             :       "     ON (am.purse_pub = pm.purse_pub AND"
    2514             :       "         am.reserve_pub = pm.reserve_pub)"
    2515             :       " WHERE pm.reserve_pub=$1"
    2516             :       "  AND pm.merge_timestamp >= $2"
    2517             :       "  AND pm.partner_serial_id=0" /* must be local! */
    2518             :       "  AND pr.finished"
    2519             :       "  AND NOT pr.refunded;",
    2520             :       2),
    2521             :     /* Used in #postgres_get_reserve_history() */
    2522           0 :     GNUNET_PQ_make_prepare (
    2523             :       "history_by_reserve",
    2524             :       "SELECT"
    2525             :       " history_fee_val"
    2526             :       ",history_fee_frac"
    2527             :       ",request_timestamp"
    2528             :       ",reserve_sig"
    2529             :       " FROM history_requests"
    2530             :       " WHERE reserve_pub=$1;",
    2531             :       1),
    2532             :     /* Used in #postgres_get_reserve_status() */
    2533           0 :     GNUNET_PQ_make_prepare (
    2534             :       "history_by_reserve_truncated",
    2535             :       "SELECT"
    2536             :       " history_fee_val"
    2537             :       ",history_fee_frac"
    2538             :       ",request_timestamp"
    2539             :       ",reserve_sig"
    2540             :       " FROM history_requests"
    2541             :       " WHERE reserve_pub=$1"
    2542             :       "  AND request_timestamp>=$2;",
    2543             :       2),
    2544             :     /* Used in #postgres_get_expired_reserves() */
    2545           0 :     GNUNET_PQ_make_prepare (
    2546             :       "get_expired_reserves",
    2547             :       "WITH ed AS MATERIALIZED ( "
    2548             :       " SELECT * "
    2549             :       " FROM reserves "
    2550             :       " WHERE expiration_date <= $1 "
    2551             :       "   AND (current_balance_val != 0 OR current_balance_frac != 0) "
    2552             :       " ORDER BY expiration_date ASC "
    2553             :       " LIMIT 1 "
    2554             :       ") "
    2555             :       "SELECT "
    2556             :       " ed.expiration_date "
    2557             :       " ,payto_uri AS account_details "
    2558             :       " ,ed.reserve_pub "
    2559             :       " ,current_balance_val "
    2560             :       " ,current_balance_frac "
    2561             :       "FROM ( "
    2562             :       " SELECT "
    2563             :       "  * "
    2564             :       " FROM reserves_in "
    2565             :       " WHERE reserve_pub = ( "
    2566             :       "     SELECT reserve_pub FROM ed) "
    2567             :       " ) ri "
    2568             :       "JOIN wire_targets wt ON (ri.wire_source_h_payto = wt.wire_target_h_payto) "
    2569             :       "JOIN ed ON (ri.reserve_pub = ed.reserve_pub); ",
    2570             :       1),
    2571             :     /* Used in #postgres_get_coin_transactions() to obtain recoup transactions
    2572             :        for a coin */
    2573           0 :     GNUNET_PQ_make_prepare (
    2574             :       "recoup_by_coin",
    2575             :       "SELECT"
    2576             :       " reserves.reserve_pub"
    2577             :       ",denoms.denom_pub_hash"
    2578             :       ",coin_sig"
    2579             :       ",coin_blind"
    2580             :       ",amount_val"
    2581             :       ",amount_frac"
    2582             :       ",recoup_timestamp"
    2583             :       ",recoup_uuid"
    2584             :       " FROM recoup rcp"
    2585             :       /* NOTE: suboptimal JOIN follows: crosses shards!
    2586             :          Could theoretically be improved via a materialized
    2587             :          index. But likely not worth it (query is rare and
    2588             :          number of reserve shards might be limited) */
    2589             :       " JOIN reserves_out ro"
    2590             :       "   USING (reserve_out_serial_id)"
    2591             :       " JOIN reserves"
    2592             :       "   USING (reserve_uuid)"
    2593             :       " JOIN known_coins coins"
    2594             :       "   USING (coin_pub)"
    2595             :       " JOIN denominations denoms"
    2596             :       "   ON (denoms.denominations_serial = coins.denominations_serial)"
    2597             :       " WHERE coins.coin_pub=$1;",
    2598             :       1),
    2599             :     /* Used in #postgres_get_coin_transactions() to obtain recoup transactions
    2600             :        for a refreshed coin */
    2601           0 :     GNUNET_PQ_make_prepare (
    2602             :       "recoup_by_refreshed_coin",
    2603             :       "SELECT"
    2604             :       " old_coins.coin_pub AS old_coin_pub"
    2605             :       ",coin_sig"
    2606             :       ",coin_blind"
    2607             :       ",amount_val"
    2608             :       ",amount_frac"
    2609             :       ",recoup_timestamp"
    2610             :       ",denoms.denom_pub_hash"
    2611             :       ",coins.denom_sig"
    2612             :       ",recoup_refresh_uuid"
    2613             :       " FROM recoup_refresh"
    2614             :       "    JOIN refresh_revealed_coins rrc"
    2615             :       "      USING (rrc_serial)"
    2616             :       "    JOIN refresh_commitments rfc"
    2617             :       "      ON (rrc.melt_serial_id = rfc.melt_serial_id)"
    2618             :       "    JOIN known_coins old_coins"
    2619             :       "      ON (rfc.old_coin_pub = old_coins.coin_pub)"
    2620             :       "    JOIN known_coins coins"
    2621             :       "      ON (recoup_refresh.coin_pub = coins.coin_pub)"
    2622             :       "    JOIN denominations denoms"
    2623             :       "      ON (denoms.denominations_serial = coins.denominations_serial)"
    2624             :       " WHERE coins.coin_pub=$1;",
    2625             :       1),
    2626             :     /* Used in #postgres_get_reserve_by_h_blind() */
    2627           0 :     GNUNET_PQ_make_prepare (
    2628             :       "reserve_by_h_blind",
    2629             :       "SELECT"
    2630             :       " reserves.reserve_pub"
    2631             :       ",reserve_out_serial_id"
    2632             :       " FROM reserves_out"
    2633             :       " JOIN reserves"
    2634             :       "   USING (reserve_uuid)"
    2635             :       " WHERE h_blind_ev=$1"
    2636             :       " LIMIT 1;",
    2637             :       1),
    2638             :     /* Used in #postgres_get_old_coin_by_h_blind() */
    2639           0 :     GNUNET_PQ_make_prepare (
    2640             :       "old_coin_by_h_blind",
    2641             :       "SELECT"
    2642             :       " okc.coin_pub AS old_coin_pub"
    2643             :       ",rrc_serial"
    2644             :       " FROM refresh_revealed_coins rrc"
    2645             :       " JOIN refresh_commitments rcom USING (melt_serial_id)"
    2646             :       " JOIN known_coins okc ON (rcom.old_coin_pub = okc.coin_pub)"
    2647             :       " WHERE h_coin_ev=$1"
    2648             :       " LIMIT 1;",
    2649             :       1),
    2650             :     /* Used in #postgres_lookup_auditor_timestamp() */
    2651           0 :     GNUNET_PQ_make_prepare (
    2652             :       "lookup_auditor_timestamp",
    2653             :       "SELECT"
    2654             :       " last_change"
    2655             :       " FROM auditors"
    2656             :       " WHERE auditor_pub=$1;",
    2657             :       1),
    2658             :     /* Used in #postgres_lookup_auditor_status() */
    2659           0 :     GNUNET_PQ_make_prepare (
    2660             :       "lookup_auditor_status",
    2661             :       "SELECT"
    2662             :       " auditor_url"
    2663             :       ",is_active"
    2664             :       " FROM auditors"
    2665             :       " WHERE auditor_pub=$1;",
    2666             :       1),
    2667             :     /* Used in #postgres_lookup_wire_timestamp() */
    2668           0 :     GNUNET_PQ_make_prepare (
    2669             :       "lookup_wire_timestamp",
    2670             :       "SELECT"
    2671             :       " last_change"
    2672             :       " FROM wire_accounts"
    2673             :       " WHERE payto_uri=$1;",
    2674             :       1),
    2675             :     /* used in #postgres_insert_auditor() */
    2676           0 :     GNUNET_PQ_make_prepare (
    2677             :       "insert_auditor",
    2678             :       "INSERT INTO auditors "
    2679             :       "(auditor_pub"
    2680             :       ",auditor_name"
    2681             :       ",auditor_url"
    2682             :       ",is_active"
    2683             :       ",last_change"
    2684             :       ") VALUES "
    2685             :       "($1, $2, $3, true, $4);",
    2686             :       4),
    2687             :     /* used in #postgres_update_auditor() */
    2688           0 :     GNUNET_PQ_make_prepare (
    2689             :       "update_auditor",
    2690             :       "UPDATE auditors"
    2691             :       " SET"
    2692             :       "  auditor_url=$2"
    2693             :       " ,auditor_name=$3"
    2694             :       " ,is_active=$4"
    2695             :       " ,last_change=$5"
    2696             :       " WHERE auditor_pub=$1",
    2697             :       5),
    2698             :     /* used in #postgres_insert_wire() */
    2699           0 :     GNUNET_PQ_make_prepare (
    2700             :       "insert_wire",
    2701             :       "INSERT INTO wire_accounts "
    2702             :       "(payto_uri"
    2703             :       ",master_sig"
    2704             :       ",is_active"
    2705             :       ",last_change"
    2706             :       ") VALUES "
    2707             :       "($1, $2, true, $3);",
    2708             :       3),
    2709             :     /* used in #postgres_update_wire() */
    2710           0 :     GNUNET_PQ_make_prepare (
    2711             :       "update_wire",
    2712             :       "UPDATE wire_accounts"
    2713             :       " SET"
    2714             :       "  is_active=$2"
    2715             :       " ,last_change=$3"
    2716             :       " WHERE payto_uri=$1",
    2717             :       3),
    2718             :     /* used in #postgres_update_wire() */
    2719           0 :     GNUNET_PQ_make_prepare (
    2720             :       "get_wire_accounts",
    2721             :       "SELECT"
    2722             :       " payto_uri"
    2723             :       ",master_sig"
    2724             :       " FROM wire_accounts"
    2725             :       " WHERE is_active",
    2726             :       0),
    2727             :     /* used in #postgres_update_wire() */
    2728           0 :     GNUNET_PQ_make_prepare (
    2729             :       "get_wire_fees",
    2730             :       "SELECT"
    2731             :       " wire_fee_val"
    2732             :       ",wire_fee_frac"
    2733             :       ",closing_fee_val"
    2734             :       ",closing_fee_frac"
    2735             :       ",wad_fee_val"
    2736             :       ",wad_fee_frac"
    2737             :       ",start_date"
    2738             :       ",end_date"
    2739             :       ",master_sig"
    2740             :       " FROM wire_fee"
    2741             :       " WHERE wire_method=$1",
    2742             :       1),
    2743             :     /* used in #postgres_insert_signkey_revocation() */
    2744           0 :     GNUNET_PQ_make_prepare (
    2745             :       "insert_signkey_revocation",
    2746             :       "INSERT INTO signkey_revocations "
    2747             :       "(esk_serial"
    2748             :       ",master_sig"
    2749             :       ") SELECT esk_serial, $2 "
    2750             :       "    FROM exchange_sign_keys"
    2751             :       "   WHERE exchange_pub=$1;",
    2752             :       2),
    2753             :     /* used in #postgres_insert_signkey_revocation() */
    2754           0 :     GNUNET_PQ_make_prepare (
    2755             :       "lookup_signkey_revocation",
    2756             :       "SELECT "
    2757             :       " master_sig"
    2758             :       " FROM signkey_revocations"
    2759             :       " WHERE esk_serial="
    2760             :       "   (SELECT esk_serial"
    2761             :       "      FROM exchange_sign_keys"
    2762             :       "     WHERE exchange_pub=$1);",
    2763             :       1),
    2764             :     /* used in #postgres_insert_signkey() */
    2765           0 :     GNUNET_PQ_make_prepare (
    2766             :       "insert_signkey",
    2767             :       "INSERT INTO exchange_sign_keys "
    2768             :       "(exchange_pub"
    2769             :       ",valid_from"
    2770             :       ",expire_sign"
    2771             :       ",expire_legal"
    2772             :       ",master_sig"
    2773             :       ") VALUES "
    2774             :       "($1, $2, $3, $4, $5);",
    2775             :       5),
    2776             :     /* used in #postgres_lookup_signing_key() */
    2777           0 :     GNUNET_PQ_make_prepare (
    2778             :       "lookup_signing_key",
    2779             :       "SELECT"
    2780             :       " valid_from"
    2781             :       ",expire_sign"
    2782             :       ",expire_legal"
    2783             :       " FROM exchange_sign_keys"
    2784             :       " WHERE exchange_pub=$1",
    2785             :       1),
    2786             :     /* used in #postgres_lookup_denomination_key() */
    2787           0 :     GNUNET_PQ_make_prepare (
    2788             :       "lookup_denomination_key",
    2789             :       "SELECT"
    2790             :       " valid_from"
    2791             :       ",expire_withdraw"
    2792             :       ",expire_deposit"
    2793             :       ",expire_legal"
    2794             :       ",coin_val"
    2795             :       ",coin_frac"
    2796             :       ",fee_withdraw_val"
    2797             :       ",fee_withdraw_frac"
    2798             :       ",fee_deposit_val"
    2799             :       ",fee_deposit_frac"
    2800             :       ",fee_refresh_val"
    2801             :       ",fee_refresh_frac"
    2802             :       ",fee_refund_val"
    2803             :       ",fee_refund_frac"
    2804             :       ",age_mask"
    2805             :       " FROM denominations"
    2806             :       " WHERE denom_pub_hash=$1;",
    2807             :       1),
    2808             :     /* used in #postgres_insert_auditor_denom_sig() */
    2809           0 :     GNUNET_PQ_make_prepare (
    2810             :       "insert_auditor_denom_sig",
    2811             :       "WITH ax AS"
    2812             :       " (SELECT auditor_uuid"
    2813             :       "    FROM auditors"
    2814             :       "   WHERE auditor_pub=$1)"
    2815             :       "INSERT INTO auditor_denom_sigs "
    2816             :       "(auditor_uuid"
    2817             :       ",denominations_serial"
    2818             :       ",auditor_sig"
    2819             :       ") SELECT ax.auditor_uuid, denominations_serial, $3 "
    2820             :       "    FROM denominations"
    2821             :       "   CROSS JOIN ax"
    2822             :       "   WHERE denom_pub_hash=$2;",
    2823             :       3),
    2824             :     /* used in #postgres_select_auditor_denom_sig() */
    2825           0 :     GNUNET_PQ_make_prepare (
    2826             :       "select_auditor_denom_sig",
    2827             :       "SELECT"
    2828             :       " auditor_sig"
    2829             :       " FROM auditor_denom_sigs"
    2830             :       " WHERE auditor_uuid="
    2831             :       "  (SELECT auditor_uuid"
    2832             :       "    FROM auditors"
    2833             :       "    WHERE auditor_pub=$1)"
    2834             :       " AND denominations_serial="
    2835             :       "  (SELECT denominations_serial"
    2836             :       "    FROM denominations"
    2837             :       "    WHERE denom_pub_hash=$2);",
    2838             :       2),
    2839             :     /* used in #postgres_lookup_wire_fee_by_time() */
    2840           0 :     GNUNET_PQ_make_prepare (
    2841             :       "lookup_wire_fee_by_time",
    2842             :       "SELECT"
    2843             :       " wire_fee_val"
    2844             :       ",wire_fee_frac"
    2845             :       ",closing_fee_val"
    2846             :       ",closing_fee_frac"
    2847             :       ",wad_fee_val"
    2848             :       ",wad_fee_frac"
    2849             :       " FROM wire_fee"
    2850             :       " WHERE wire_method=$1"
    2851             :       " AND end_date > $2"
    2852             :       " AND start_date < $3;",
    2853             :       1),
    2854             :     /* used in #postgres_lookup_wire_fee_by_time() */
    2855           0 :     GNUNET_PQ_make_prepare (
    2856             :       "lookup_global_fee_by_time",
    2857             :       "SELECT"
    2858             :       " history_fee_val"
    2859             :       ",history_fee_frac"
    2860             :       ",kyc_fee_val"
    2861             :       ",kyc_fee_frac"
    2862             :       ",account_fee_val"
    2863             :       ",account_fee_frac"
    2864             :       ",purse_fee_val"
    2865             :       ",purse_fee_frac"
    2866             :       ",purse_timeout"
    2867             :       ",kyc_timeout"
    2868             :       ",history_expiration"
    2869             :       ",purse_account_limit"
    2870             :       " FROM global_fee"
    2871             :       " WHERE end_date > $1"
    2872             :       "   AND start_date < $2;",
    2873             :       1),
    2874             :     /* used in #postgres_commit */
    2875           0 :     GNUNET_PQ_make_prepare (
    2876             :       "do_commit",
    2877             :       "COMMIT",
    2878             :       0),
    2879             :     /* used in #postgres_lookup_serial_by_table() */
    2880           0 :     GNUNET_PQ_make_prepare (
    2881             :       "select_serial_by_table_denominations",
    2882             :       "SELECT"
    2883             :       " denominations_serial AS serial"
    2884             :       " FROM denominations"
    2885             :       " ORDER BY denominations_serial DESC"
    2886             :       " LIMIT 1;",
    2887             :       0),
    2888           0 :     GNUNET_PQ_make_prepare (
    2889             :       "select_serial_by_table_denomination_revocations",
    2890             :       "SELECT"
    2891             :       " denom_revocations_serial_id AS serial"
    2892             :       " FROM denomination_revocations"
    2893             :       " ORDER BY denom_revocations_serial_id DESC"
    2894             :       " LIMIT 1;",
    2895             :       0),
    2896           0 :     GNUNET_PQ_make_prepare (
    2897             :       "select_serial_by_table_wire_targets",
    2898             :       "SELECT"
    2899             :       " wire_target_serial_id AS serial"
    2900             :       " FROM wire_targets"
    2901             :       " ORDER BY wire_target_serial_id DESC"
    2902             :       " LIMIT 1;",
    2903             :       0),
    2904           0 :     GNUNET_PQ_make_prepare (
    2905             :       "select_serial_by_table_reserves",
    2906             :       "SELECT"
    2907             :       " reserve_uuid AS serial"
    2908             :       " FROM reserves"
    2909             :       " ORDER BY reserve_uuid DESC"
    2910             :       " LIMIT 1;",
    2911             :       0),
    2912           0 :     GNUNET_PQ_make_prepare (
    2913             :       "select_serial_by_table_reserves_in",
    2914             :       "SELECT"
    2915             :       " reserve_in_serial_id AS serial"
    2916             :       " FROM reserves_in"
    2917             :       " ORDER BY reserve_in_serial_id DESC"
    2918             :       " LIMIT 1;",
    2919             :       0),
    2920           0 :     GNUNET_PQ_make_prepare (
    2921             :       "select_serial_by_table_reserves_close",
    2922             :       "SELECT"
    2923             :       " close_uuid AS serial"
    2924             :       " FROM reserves_close"
    2925             :       " ORDER BY close_uuid DESC"
    2926             :       " LIMIT 1;",
    2927             :       0),
    2928           0 :     GNUNET_PQ_make_prepare (
    2929             :       "select_serial_by_table_reserves_out",
    2930             :       "SELECT"
    2931             :       " reserve_out_serial_id AS serial"
    2932             :       " FROM reserves_out"
    2933             :       " ORDER BY reserve_out_serial_id DESC"
    2934             :       " LIMIT 1;",
    2935             :       0),
    2936           0 :     GNUNET_PQ_make_prepare (
    2937             :       "select_serial_by_table_auditors",
    2938             :       "SELECT"
    2939             :       " auditor_uuid AS serial"
    2940             :       " FROM auditors"
    2941             :       " ORDER BY auditor_uuid DESC"
    2942             :       " LIMIT 1;",
    2943             :       0),
    2944           0 :     GNUNET_PQ_make_prepare (
    2945             :       "select_serial_by_table_auditor_denom_sigs",
    2946             :       "SELECT"
    2947             :       " auditor_denom_serial AS serial"
    2948             :       " FROM auditor_denom_sigs"
    2949             :       " ORDER BY auditor_denom_serial DESC"
    2950             :       " LIMIT 1;",
    2951             :       0),
    2952           0 :     GNUNET_PQ_make_prepare (
    2953             :       "select_serial_by_table_exchange_sign_keys",
    2954             :       "SELECT"
    2955             :       " esk_serial AS serial"
    2956             :       " FROM exchange_sign_keys"
    2957             :       " ORDER BY esk_serial DESC"
    2958             :       " LIMIT 1;",
    2959             :       0),
    2960           0 :     GNUNET_PQ_make_prepare (
    2961             :       "select_serial_by_table_signkey_revocations",
    2962             :       "SELECT"
    2963             :       " signkey_revocations_serial_id AS serial"
    2964             :       " FROM signkey_revocations"
    2965             :       " ORDER BY signkey_revocations_serial_id DESC"
    2966             :       " LIMIT 1;",
    2967             :       0),
    2968           0 :     GNUNET_PQ_make_prepare (
    2969             :       "select_serial_by_table_known_coins",
    2970             :       "SELECT"
    2971             :       " known_coin_id AS serial"
    2972             :       " FROM known_coins"
    2973             :       " ORDER BY known_coin_id DESC"
    2974             :       " LIMIT 1;",
    2975             :       0),
    2976           0 :     GNUNET_PQ_make_prepare (
    2977             :       "select_serial_by_table_refresh_commitments",
    2978             :       "SELECT"
    2979             :       " melt_serial_id AS serial"
    2980             :       " FROM refresh_commitments"
    2981             :       " ORDER BY melt_serial_id DESC"
    2982             :       " LIMIT 1;",
    2983             :       0),
    2984           0 :     GNUNET_PQ_make_prepare (
    2985             :       "select_serial_by_table_refresh_revealed_coins",
    2986             :       "SELECT"
    2987             :       " rrc_serial AS serial"
    2988             :       " FROM refresh_revealed_coins"
    2989             :       " ORDER BY rrc_serial DESC"
    2990             :       " LIMIT 1;",
    2991             :       0),
    2992           0 :     GNUNET_PQ_make_prepare (
    2993             :       "select_serial_by_table_refresh_transfer_keys",
    2994             :       "SELECT"
    2995             :       " rtc_serial AS serial"
    2996             :       " FROM refresh_transfer_keys"
    2997             :       " ORDER BY rtc_serial DESC"
    2998             :       " LIMIT 1;",
    2999             :       0),
    3000           0 :     GNUNET_PQ_make_prepare (
    3001             :       "select_serial_by_table_deposits",
    3002             :       "SELECT"
    3003             :       " deposit_serial_id AS serial"
    3004             :       " FROM deposits"
    3005             :       " ORDER BY deposit_serial_id DESC"
    3006             :       " LIMIT 1;",
    3007             :       0),
    3008           0 :     GNUNET_PQ_make_prepare (
    3009             :       "select_serial_by_table_refunds",
    3010             :       "SELECT"
    3011             :       " refund_serial_id AS serial"
    3012             :       " FROM refunds"
    3013             :       " ORDER BY refund_serial_id DESC"
    3014             :       " LIMIT 1;",
    3015             :       0),
    3016           0 :     GNUNET_PQ_make_prepare (
    3017             :       "select_serial_by_table_wire_out",
    3018             :       "SELECT"
    3019             :       " wireout_uuid AS serial"
    3020             :       " FROM wire_out"
    3021             :       " ORDER BY wireout_uuid DESC"
    3022             :       " LIMIT 1;",
    3023             :       0),
    3024           0 :     GNUNET_PQ_make_prepare (
    3025             :       "select_serial_by_table_aggregation_tracking",
    3026             :       "SELECT"
    3027             :       " aggregation_serial_id AS serial"
    3028             :       " FROM aggregation_tracking"
    3029             :       " ORDER BY aggregation_serial_id DESC"
    3030             :       " LIMIT 1;",
    3031             :       0),
    3032           0 :     GNUNET_PQ_make_prepare (
    3033             :       "select_serial_by_table_wire_fee",
    3034             :       "SELECT"
    3035             :       " wire_fee_serial AS serial"
    3036             :       " FROM wire_fee"
    3037             :       " ORDER BY wire_fee_serial DESC"
    3038             :       " LIMIT 1;",
    3039             :       0),
    3040           0 :     GNUNET_PQ_make_prepare (
    3041             :       "select_serial_by_table_global_fee",
    3042             :       "SELECT"
    3043             :       " global_fee_serial AS serial"
    3044             :       " FROM global_fee"
    3045             :       " ORDER BY global_fee_serial DESC"
    3046             :       " LIMIT 1;",
    3047             :       0),
    3048           0 :     GNUNET_PQ_make_prepare (
    3049             :       "select_serial_by_table_recoup",
    3050             :       "SELECT"
    3051             :       " recoup_uuid AS serial"
    3052             :       " FROM recoup"
    3053             :       " ORDER BY recoup_uuid DESC"
    3054             :       " LIMIT 1;",
    3055             :       0),
    3056           0 :     GNUNET_PQ_make_prepare (
    3057             :       "select_serial_by_table_recoup_refresh",
    3058             :       "SELECT"
    3059             :       " recoup_refresh_uuid AS serial"
    3060             :       " FROM recoup_refresh"
    3061             :       " ORDER BY recoup_refresh_uuid DESC"
    3062             :       " LIMIT 1;",
    3063             :       0),
    3064           0 :     GNUNET_PQ_make_prepare (
    3065             :       "select_serial_by_table_extensions",
    3066             :       "SELECT"
    3067             :       " extension_id AS serial"
    3068             :       " FROM extensions"
    3069             :       " ORDER BY extension_id DESC"
    3070             :       " LIMIT 1;",
    3071             :       0),
    3072           0 :     GNUNET_PQ_make_prepare (
    3073             :       "select_serial_by_table_extension_details",
    3074             :       "SELECT"
    3075             :       " extension_details_serial_id AS serial"
    3076             :       " FROM extension_details"
    3077             :       " ORDER BY extension_details_serial_id DESC"
    3078             :       " LIMIT 1;",
    3079             :       0),
    3080           0 :     GNUNET_PQ_make_prepare (
    3081             :       "select_serial_by_table_purse_requests",
    3082             :       "SELECT"
    3083             :       " purse_requests_serial_id AS serial"
    3084             :       " FROM purse_requests"
    3085             :       " ORDER BY purse_requests_serial_id DESC"
    3086             :       " LIMIT 1;",
    3087             :       0),
    3088           0 :     GNUNET_PQ_make_prepare (
    3089             :       "select_serial_by_table_purse_refunds",
    3090             :       "SELECT"
    3091             :       " purse_refunds_serial_id AS serial"
    3092             :       " FROM purse_refunds"
    3093             :       " ORDER BY purse_refunds_serial_id DESC"
    3094             :       " LIMIT 1;",
    3095             :       0),
    3096           0 :     GNUNET_PQ_make_prepare (
    3097             :       "select_serial_by_table_purse_merges",
    3098             :       "SELECT"
    3099             :       " purse_merge_request_serial_id AS serial"
    3100             :       " FROM purse_merges"
    3101             :       " ORDER BY purse_merge_request_serial_id DESC"
    3102             :       " LIMIT 1;",
    3103             :       0),
    3104           0 :     GNUNET_PQ_make_prepare (
    3105             :       "select_serial_by_table_purse_deposits",
    3106             :       "SELECT"
    3107             :       " purse_deposit_serial_id AS serial"
    3108             :       " FROM purse_deposits"
    3109             :       " ORDER BY purse_deposit_serial_id DESC"
    3110             :       " LIMIT 1;",
    3111             :       0),
    3112           0 :     GNUNET_PQ_make_prepare (
    3113             :       "select_serial_by_table_account_merges",
    3114             :       "SELECT"
    3115             :       " account_merge_request_serial_id AS serial"
    3116             :       " FROM account_merges"
    3117             :       " ORDER BY account_merge_request_serial_id DESC"
    3118             :       " LIMIT 1;",
    3119             :       0),
    3120           0 :     GNUNET_PQ_make_prepare (
    3121             :       "select_serial_by_table_history_requests",
    3122             :       "SELECT"
    3123             :       " history_request_serial_id AS serial"
    3124             :       " FROM history_requests"
    3125             :       " ORDER BY history_request_serial_id DESC"
    3126             :       " LIMIT 1;",
    3127             :       0),
    3128           0 :     GNUNET_PQ_make_prepare (
    3129             :       "select_serial_by_table_close_requests",
    3130             :       "SELECT"
    3131             :       " close_request_serial_id AS serial"
    3132             :       " FROM close_requests"
    3133             :       " ORDER BY close_request_serial_id DESC"
    3134             :       " LIMIT 1;",
    3135             :       0),
    3136           0 :     GNUNET_PQ_make_prepare (
    3137             :       "select_serial_by_table_wads_out",
    3138             :       "SELECT"
    3139             :       " wad_out_serial_id AS serial"
    3140             :       " FROM wads_out"
    3141             :       " ORDER BY wad_out_serial_id DESC"
    3142             :       " LIMIT 1;",
    3143             :       0),
    3144           0 :     GNUNET_PQ_make_prepare (
    3145             :       "select_serial_by_table_wads_out_entries",
    3146             :       "SELECT"
    3147             :       " wad_out_entry_serial_id AS serial"
    3148             :       " FROM wad_out_entries"
    3149             :       " ORDER BY wad_out_entry_serial_id DESC"
    3150             :       " LIMIT 1;",
    3151             :       0),
    3152           0 :     GNUNET_PQ_make_prepare (
    3153             :       "select_serial_by_table_wads_in",
    3154             :       "SELECT"
    3155             :       " wad_in_serial_id AS serial"
    3156             :       " FROM wads_in"
    3157             :       " ORDER BY wad_in_serial_id DESC"
    3158             :       " LIMIT 1;",
    3159             :       0),
    3160           0 :     GNUNET_PQ_make_prepare (
    3161             :       "select_serial_by_table_wads_in_entries",
    3162             :       "SELECT"
    3163             :       " wad_in_entry_serial_id AS serial"
    3164             :       " FROM wad_in_entries"
    3165             :       " ORDER BY wad_in_entry_serial_id DESC"
    3166             :       " LIMIT 1;",
    3167             :       0),
    3168           0 :     GNUNET_PQ_make_prepare (
    3169             :       "select_serial_by_table_profit_drains",
    3170             :       "SELECT"
    3171             :       " profit_drain_serial_id AS serial"
    3172             :       " FROM profit_drains"
    3173             :       " ORDER BY profit_drain_serial_id DESC"
    3174             :       " LIMIT 1;",
    3175             :       0),
    3176             :     /* For postgres_lookup_records_by_table */
    3177           0 :     GNUNET_PQ_make_prepare (
    3178             :       "select_above_serial_by_table_denominations",
    3179             :       "SELECT"
    3180             :       " denominations_serial AS serial"
    3181             :       ",denom_type"
    3182             :       ",denom_pub"
    3183             :       ",master_sig"
    3184             :       ",valid_from"
    3185             :       ",expire_withdraw"
    3186             :       ",expire_deposit"
    3187             :       ",expire_legal"
    3188             :       ",coin_val"
    3189             :       ",coin_frac"
    3190             :       ",fee_withdraw_val"
    3191             :       ",fee_withdraw_frac"
    3192             :       ",fee_deposit_val"
    3193             :       ",fee_deposit_frac"
    3194             :       ",fee_refresh_val"
    3195             :       ",fee_refresh_frac"
    3196             :       ",fee_refund_val"
    3197             :       ",fee_refund_frac"
    3198             :       ",age_mask"
    3199             :       " FROM denominations"
    3200             :       " WHERE denominations_serial > $1"
    3201             :       " ORDER BY denominations_serial ASC;",
    3202             :       1),
    3203           0 :     GNUNET_PQ_make_prepare (
    3204             :       "select_above_serial_by_table_denomination_revocations",
    3205             :       "SELECT"
    3206             :       " denom_revocations_serial_id AS serial"
    3207             :       ",master_sig"
    3208             :       ",denominations_serial"
    3209             :       " FROM denomination_revocations"
    3210             :       " WHERE denom_revocations_serial_id > $1"
    3211             :       " ORDER BY denom_revocations_serial_id ASC;",
    3212             :       1),
    3213           0 :     GNUNET_PQ_make_prepare (
    3214             :       "select_above_serial_by_table_wire_targets",
    3215             :       "SELECT"
    3216             :       " wire_target_serial_id AS serial"
    3217             :       ",payto_uri"
    3218             :       " FROM wire_targets"
    3219             :       " WHERE wire_target_serial_id > $1"
    3220             :       " ORDER BY wire_target_serial_id ASC;",
    3221             :       1),
    3222           0 :     GNUNET_PQ_make_prepare (
    3223             :       "select_above_serial_by_table_reserves",
    3224             :       "SELECT"
    3225             :       " reserve_uuid AS serial"
    3226             :       ",reserve_pub"
    3227             :       ",expiration_date"
    3228             :       ",gc_date"
    3229             :       " FROM reserves"
    3230             :       " WHERE reserve_uuid > $1"
    3231             :       " ORDER BY reserve_uuid ASC;",
    3232             :       1),
    3233           0 :     GNUNET_PQ_make_prepare (
    3234             :       "select_above_serial_by_table_reserves_in",
    3235             :       "SELECT"
    3236             :       " reserve_in_serial_id AS serial"
    3237             :       ",reserve_pub"
    3238             :       ",wire_reference"
    3239             :       ",credit_val"
    3240             :       ",credit_frac"
    3241             :       ",wire_source_h_payto"
    3242             :       ",exchange_account_section"
    3243             :       ",execution_date"
    3244             :       " FROM reserves_in"
    3245             :       " WHERE reserve_in_serial_id > $1"
    3246             :       " ORDER BY reserve_in_serial_id ASC;",
    3247             :       1),
    3248           0 :     GNUNET_PQ_make_prepare (
    3249             :       "select_above_serial_by_table_reserves_close",
    3250             :       "SELECT"
    3251             :       " close_uuid AS serial"
    3252             :       ",reserve_pub"
    3253             :       ",execution_date"
    3254             :       ",wtid"
    3255             :       ",wire_target_h_payto"
    3256             :       ",amount_val"
    3257             :       ",amount_frac"
    3258             :       ",closing_fee_val"
    3259             :       ",closing_fee_frac"
    3260             :       " FROM reserves_close"
    3261             :       " WHERE close_uuid > $1"
    3262             :       " ORDER BY close_uuid ASC;",
    3263             :       1),
    3264           0 :     GNUNET_PQ_make_prepare (
    3265             :       "select_above_serial_by_table_reserves_out",
    3266             :       "SELECT"
    3267             :       " reserve_out_serial_id AS serial"
    3268             :       ",h_blind_ev"
    3269             :       ",denominations_serial"
    3270             :       ",denom_sig"
    3271             :       ",reserve_uuid"
    3272             :       ",reserve_sig"
    3273             :       ",execution_date"
    3274             :       ",amount_with_fee_val"
    3275             :       ",amount_with_fee_frac"
    3276             :       " FROM reserves_out"
    3277             :       " JOIN reserves USING (reserve_uuid)"
    3278             :       " WHERE reserve_out_serial_id > $1"
    3279             :       " ORDER BY reserve_out_serial_id ASC;",
    3280             :       1),
    3281           0 :     GNUNET_PQ_make_prepare (
    3282             :       "select_above_serial_by_table_auditors",
    3283             :       "SELECT"
    3284             :       " auditor_uuid AS serial"
    3285             :       ",auditor_pub"
    3286             :       ",auditor_name"
    3287             :       ",auditor_url"
    3288             :       ",is_active"
    3289             :       ",last_change"
    3290             :       " FROM auditors"
    3291             :       " WHERE auditor_uuid > $1"
    3292             :       " ORDER BY auditor_uuid ASC;",
    3293             :       1),
    3294           0 :     GNUNET_PQ_make_prepare (
    3295             :       "select_above_serial_by_table_auditor_denom_sigs",
    3296             :       "SELECT"
    3297             :       " auditor_denom_serial AS serial"
    3298             :       ",auditor_uuid"
    3299             :       ",denominations_serial"
    3300             :       ",auditor_sig"
    3301             :       " FROM auditor_denom_sigs"
    3302             :       " WHERE auditor_denom_serial > $1"
    3303             :       " ORDER BY auditor_denom_serial ASC;",
    3304             :       1),
    3305           0 :     GNUNET_PQ_make_prepare (
    3306             :       "select_above_serial_by_table_exchange_sign_keys",
    3307             :       "SELECT"
    3308             :       " esk_serial AS serial"
    3309             :       ",exchange_pub"
    3310             :       ",master_sig"
    3311             :       ",valid_from"
    3312             :       ",expire_sign"
    3313             :       ",expire_legal"
    3314             :       " FROM exchange_sign_keys"
    3315             :       " WHERE esk_serial > $1"
    3316             :       " ORDER BY esk_serial ASC;",
    3317             :       1),
    3318           0 :     GNUNET_PQ_make_prepare (
    3319             :       "select_above_serial_by_table_signkey_revocations",
    3320             :       "SELECT"
    3321             :       " signkey_revocations_serial_id AS serial"
    3322             :       ",esk_serial"
    3323             :       ",master_sig"
    3324             :       " FROM signkey_revocations"
    3325             :       " WHERE signkey_revocations_serial_id > $1"
    3326             :       " ORDER BY signkey_revocations_serial_id ASC;",
    3327             :       1),
    3328           0 :     GNUNET_PQ_make_prepare (
    3329             :       "select_above_serial_by_table_known_coins",
    3330             :       "SELECT"
    3331             :       " known_coin_id AS serial"
    3332             :       ",coin_pub"
    3333             :       ",denom_sig"
    3334             :       ",denominations_serial"
    3335             :       " FROM known_coins"
    3336             :       " WHERE known_coin_id > $1"
    3337             :       " ORDER BY known_coin_id ASC;",
    3338             :       1),
    3339           0 :     GNUNET_PQ_make_prepare (
    3340             :       "select_above_serial_by_table_refresh_commitments",
    3341             :       "SELECT"
    3342             :       " melt_serial_id AS serial"
    3343             :       ",rc"
    3344             :       ",old_coin_sig"
    3345             :       ",amount_with_fee_val"
    3346             :       ",amount_with_fee_frac"
    3347             :       ",noreveal_index"
    3348             :       ",old_coin_pub"
    3349             :       " FROM refresh_commitments"
    3350             :       " WHERE melt_serial_id > $1"
    3351             :       " ORDER BY melt_serial_id ASC;",
    3352             :       1),
    3353           0 :     GNUNET_PQ_make_prepare (
    3354             :       "select_above_serial_by_table_refresh_revealed_coins",
    3355             :       "SELECT"
    3356             :       " rrc_serial AS serial"
    3357             :       ",freshcoin_index"
    3358             :       ",link_sig"
    3359             :       ",coin_ev"
    3360             :       ",ev_sig"
    3361             :       ",ewv"
    3362             :       ",denominations_serial"
    3363             :       ",melt_serial_id"
    3364             :       " FROM refresh_revealed_coins"
    3365             :       " WHERE rrc_serial > $1"
    3366             :       " ORDER BY rrc_serial ASC;",
    3367             :       1),
    3368           0 :     GNUNET_PQ_make_prepare (
    3369             :       "select_above_serial_by_table_refresh_transfer_keys",
    3370             :       "SELECT"
    3371             :       " rtc_serial AS serial"
    3372             :       ",transfer_pub"
    3373             :       ",transfer_privs"
    3374             :       ",melt_serial_id"
    3375             :       " FROM refresh_transfer_keys"
    3376             :       " WHERE rtc_serial > $1"
    3377             :       " ORDER BY rtc_serial ASC;",
    3378             :       1),
    3379           0 :     GNUNET_PQ_make_prepare (
    3380             :       "select_above_serial_by_table_deposits",
    3381             :       "SELECT"
    3382             :       " deposit_serial_id AS serial"
    3383             :       ",shard"
    3384             :       ",coin_pub"
    3385             :       ",known_coin_id"
    3386             :       ",amount_with_fee_val"
    3387             :       ",amount_with_fee_frac"
    3388             :       ",wallet_timestamp"
    3389             :       ",exchange_timestamp"
    3390             :       ",refund_deadline"
    3391             :       ",wire_deadline"
    3392             :       ",merchant_pub"
    3393             :       ",h_contract_terms"
    3394             :       ",coin_sig"
    3395             :       ",wire_salt"
    3396             :       ",wire_target_h_payto"
    3397             :       ",done"
    3398             :       ",extension_blocked"
    3399             :       ",extension_details_serial_id"
    3400             :       " FROM deposits"
    3401             :       " WHERE deposit_serial_id > $1"
    3402             :       " ORDER BY deposit_serial_id ASC;",
    3403             :       1),
    3404           0 :     GNUNET_PQ_make_prepare (
    3405             :       "select_above_serial_by_table_refunds",
    3406             :       "SELECT"
    3407             :       " refund_serial_id AS serial"
    3408             :       ",coin_pub"
    3409             :       ",merchant_sig"
    3410             :       ",rtransaction_id"
    3411             :       ",amount_with_fee_val"
    3412             :       ",amount_with_fee_frac"
    3413             :       ",deposit_serial_id"
    3414             :       " FROM refunds"
    3415             :       " WHERE refund_serial_id > $1"
    3416             :       " ORDER BY refund_serial_id ASC;",
    3417             :       1),
    3418           0 :     GNUNET_PQ_make_prepare (
    3419             :       "select_above_serial_by_table_wire_out",
    3420             :       "SELECT"
    3421             :       " wireout_uuid AS serial"
    3422             :       ",execution_date"
    3423             :       ",wtid_raw"
    3424             :       ",wire_target_h_payto"
    3425             :       ",exchange_account_section"
    3426             :       ",amount_val"
    3427             :       ",amount_frac"
    3428             :       " FROM wire_out"
    3429             :       " WHERE wireout_uuid > $1"
    3430             :       " ORDER BY wireout_uuid ASC;",
    3431             :       1),
    3432           0 :     GNUNET_PQ_make_prepare (
    3433             :       "select_above_serial_by_table_aggregation_tracking",
    3434             :       "SELECT"
    3435             :       " aggregation_serial_id AS serial"
    3436             :       ",deposit_serial_id"
    3437             :       ",wtid_raw"
    3438             :       " FROM aggregation_tracking"
    3439             :       " WHERE aggregation_serial_id > $1"
    3440             :       " ORDER BY aggregation_serial_id ASC;",
    3441             :       1),
    3442           0 :     GNUNET_PQ_make_prepare (
    3443             :       "select_above_serial_by_table_wire_fee",
    3444             :       "SELECT"
    3445             :       " wire_fee_serial AS serial"
    3446             :       ",wire_method"
    3447             :       ",start_date"
    3448             :       ",end_date"
    3449             :       ",wire_fee_val"
    3450             :       ",wire_fee_frac"
    3451             :       ",closing_fee_val"
    3452             :       ",closing_fee_frac"
    3453             :       ",wad_fee_val"
    3454             :       ",wad_fee_frac"
    3455             :       ",master_sig"
    3456             :       " FROM wire_fee"
    3457             :       " WHERE wire_fee_serial > $1"
    3458             :       " ORDER BY wire_fee_serial ASC;",
    3459             :       1),
    3460           0 :     GNUNET_PQ_make_prepare (
    3461             :       "select_above_serial_by_table_global_fee",
    3462             :       "SELECT"
    3463             :       " global_fee_serial AS serial"
    3464             :       ",start_date"
    3465             :       ",end_date"
    3466             :       ",history_fee_val"
    3467             :       ",history_fee_frac"
    3468             :       ",kyc_fee_val"
    3469             :       ",kyc_fee_frac"
    3470             :       ",account_fee_val"
    3471             :       ",account_fee_frac"
    3472             :       ",purse_fee_val"
    3473             :       ",purse_fee_frac"
    3474             :       ",purse_timeout"
    3475             :       ",kyc_timeout"
    3476             :       ",history_expiration"
    3477             :       ",purse_account_limit"
    3478             :       ",master_sig"
    3479             :       " FROM global_fee"
    3480             :       " WHERE global_fee_serial > $1"
    3481             :       " ORDER BY global_fee_serial ASC;",
    3482             :       1),
    3483           0 :     GNUNET_PQ_make_prepare (
    3484             :       "select_above_serial_by_table_recoup",
    3485             :       "SELECT"
    3486             :       " recoup_uuid AS serial"
    3487             :       ",coin_sig"
    3488             :       ",coin_blind"
    3489             :       ",amount_val"
    3490             :       ",amount_frac"
    3491             :       ",recoup_timestamp"
    3492             :       ",coin_pub"
    3493             :       ",reserve_out_serial_id"
    3494             :       " FROM recoup"
    3495             :       " WHERE recoup_uuid > $1"
    3496             :       " ORDER BY recoup_uuid ASC;",
    3497             :       1),
    3498           0 :     GNUNET_PQ_make_prepare (
    3499             :       "select_above_serial_by_table_recoup_refresh",
    3500             :       "SELECT"
    3501             :       " recoup_refresh_uuid AS serial"
    3502             :       ",coin_sig"
    3503             :       ",coin_blind"
    3504             :       ",amount_val"
    3505             :       ",amount_frac"
    3506             :       ",recoup_timestamp"
    3507             :       ",coin_pub"
    3508             :       ",known_coin_id"
    3509             :       ",rrc_serial"
    3510             :       " FROM recoup_refresh"
    3511             :       " WHERE recoup_refresh_uuid > $1"
    3512             :       " ORDER BY recoup_refresh_uuid ASC;",
    3513             :       1),
    3514             : 
    3515             : 
    3516           0 :     GNUNET_PQ_make_prepare (
    3517             :       "select_above_serial_by_table_purse_requests",
    3518             :       "SELECT"
    3519             :       " purse_requests_serial_id"
    3520             :       ",purse_pub"
    3521             :       ",merge_pub"
    3522             :       ",purse_creation"
    3523             :       ",purse_expiration"
    3524             :       ",h_contract_terms"
    3525             :       ",age_limit"
    3526             :       ",flags"
    3527             :       ",amount_with_fee_val"
    3528             :       ",amount_with_fee_frac"
    3529             :       ",purse_fee_val"
    3530             :       ",purse_fee_frac"
    3531             :       ",purse_sig"
    3532             :       " FROM purse_requests"
    3533             :       " WHERE purse_requests_serial_id > $1"
    3534             :       " ORDER BY purse_requests_serial_id ASC;",
    3535             :       1),
    3536           0 :     GNUNET_PQ_make_prepare (
    3537             :       "select_above_serial_by_table_purse_refunds",
    3538             :       "SELECT"
    3539             :       " purse_refunds_serial_id"
    3540             :       ",purse_pub"
    3541             :       " FROM purse_refunds"
    3542             :       " WHERE purse_refunds_serial_id > $1"
    3543             :       " ORDER BY purse_refunds_serial_id ASC;",
    3544             :       1),
    3545           0 :     GNUNET_PQ_make_prepare (
    3546             :       "select_above_serial_by_table_purse_merges",
    3547             :       "SELECT"
    3548             :       " purse_merge_request_serial_id"
    3549             :       ",partner_serial_id"
    3550             :       ",reserve_pub"
    3551             :       ",purse_pub"
    3552             :       ",merge_sig"
    3553             :       ",merge_timestamp"
    3554             :       " FROM purse_merges"
    3555             :       " WHERE purse_merge_request_serial_id > $1"
    3556             :       " ORDER BY purse_merge_request_serial_id ASC;",
    3557             :       1),
    3558           0 :     GNUNET_PQ_make_prepare (
    3559             :       "select_above_serial_by_table_purse_deposits",
    3560             :       "SELECT"
    3561             :       " purse_deposit_serial_id"
    3562             :       ",partner_serial_id"
    3563             :       ",purse_pub"
    3564             :       ",coin_pub"
    3565             :       ",amount_with_fee_val"
    3566             :       ",amount_with_fee_frac"
    3567             :       ",coin_sig"
    3568             :       " FROM purse_deposits"
    3569             :       " WHERE purse_deposit_serial_id > $1"
    3570             :       " ORDER BY purse_deposit_serial_id ASC;",
    3571             :       1),
    3572           0 :     GNUNET_PQ_make_prepare (
    3573             :       "select_above_serial_by_table_account_merges",
    3574             :       "SELECT"
    3575             :       " account_merge_request_serial_id"
    3576             :       ",reserve_pub"
    3577             :       ",reserve_sig"
    3578             :       ",purse_pub"
    3579             :       " FROM account_merges"
    3580             :       " WHERE account_merge_request_serial_id > $1"
    3581             :       " ORDER BY account_merge_request_serial_id ASC;",
    3582             :       1),
    3583           0 :     GNUNET_PQ_make_prepare (
    3584             :       "select_above_serial_by_table_history_requests",
    3585             :       "SELECT"
    3586             :       " history_request_serial_id"
    3587             :       ",reserve_pub"
    3588             :       ",request_timestamp"
    3589             :       ",reserve_sig"
    3590             :       ",history_fee_val"
    3591             :       ",history_fee_frac"
    3592             :       " FROM history_requests"
    3593             :       " WHERE history_request_serial_id > $1"
    3594             :       " ORDER BY history_request_serial_id ASC;",
    3595             :       1),
    3596           0 :     GNUNET_PQ_make_prepare (
    3597             :       "select_above_serial_by_table_close_requests",
    3598             :       "SELECT"
    3599             :       " close_request_serial_id"
    3600             :       ",reserve_pub"
    3601             :       ",close_timestamp"
    3602             :       ",reserve_sig"
    3603             :       ",close_val"
    3604             :       ",close_frac"
    3605             :       " FROM close_requests"
    3606             :       " WHERE close_request_serial_id > $1"
    3607             :       " ORDER BY close_request_serial_id ASC;",
    3608             :       1),
    3609           0 :     GNUNET_PQ_make_prepare (
    3610             :       "select_above_serial_by_table_wads_out",
    3611             :       "SELECT"
    3612             :       " wad_out_serial_id"
    3613             :       ",wad_id"
    3614             :       ",partner_serial_id"
    3615             :       ",amount_val"
    3616             :       ",amount_frac"
    3617             :       ",execution_time"
    3618             :       " FROM wads_out"
    3619             :       " WHERE wad_out_serial_id > $1"
    3620             :       " ORDER BY wad_out_serial_id ASC;",
    3621             :       1),
    3622           0 :     GNUNET_PQ_make_prepare (
    3623             :       "select_above_serial_by_table_wads_out_entries",
    3624             :       "SELECT"
    3625             :       " wad_out_entry_serial_id"
    3626             :       ",reserve_pub"
    3627             :       ",purse_pub"
    3628             :       ",h_contract"
    3629             :       ",purse_expiration"
    3630             :       ",merge_timestamp"
    3631             :       ",amount_with_fee_val"
    3632             :       ",amount_with_fee_frac"
    3633             :       ",wad_fee_val"
    3634             :       ",wad_fee_frac"
    3635             :       ",deposit_fees_val"
    3636             :       ",deposit_fees_frac"
    3637             :       ",reserve_sig"
    3638             :       ",purse_sig"
    3639             :       " FROM wad_out_entries"
    3640             :       " WHERE wad_out_entry_serial_id > $1"
    3641             :       " ORDER BY wad_out_entry_serial_id ASC;",
    3642             :       1),
    3643           0 :     GNUNET_PQ_make_prepare (
    3644             :       "select_above_serial_by_table_wads_in",
    3645             :       "SELECT"
    3646             :       " wad_in_serial_id"
    3647             :       ",wad_id"
    3648             :       ",origin_exchange_url"
    3649             :       ",amount_val"
    3650             :       ",amount_frac"
    3651             :       ",arrival_time"
    3652             :       " FROM wads_in"
    3653             :       " WHERE wad_in_serial_id > $1"
    3654             :       " ORDER BY wad_in_serial_id ASC;",
    3655             :       1),
    3656           0 :     GNUNET_PQ_make_prepare (
    3657             :       "select_above_serial_by_table_wads_in_entries",
    3658             :       "SELECT"
    3659             :       " wad_in_entry_serial_id"
    3660             :       ",reserve_pub"
    3661             :       ",purse_pub"
    3662             :       ",h_contract"
    3663             :       ",purse_expiration"
    3664             :       ",merge_timestamp"
    3665             :       ",amount_with_fee_val"
    3666             :       ",amount_with_fee_frac"
    3667             :       ",wad_fee_val"
    3668             :       ",wad_fee_frac"
    3669             :       ",deposit_fees_val"
    3670             :       ",deposit_fees_frac"
    3671             :       ",reserve_sig"
    3672             :       ",purse_sig"
    3673             :       " FROM wad_in_entries"
    3674             :       " WHERE wad_in_entry_serial_id > $1"
    3675             :       " ORDER BY wad_in_entry_serial_id ASC;",
    3676             :       1),
    3677           0 :     GNUNET_PQ_make_prepare (
    3678             :       "select_above_serial_by_table_profit_drains",
    3679             :       "SELECT"
    3680             :       " profit_drain_serial_id"
    3681             :       ",wtid"
    3682             :       ",account_section"
    3683             :       ",payto_uri"
    3684             :       ",trigger_date"
    3685             :       ",amount_val"
    3686             :       ",amount_frac"
    3687             :       ",master_sig"
    3688             :       " FROM profit_drains"
    3689             :       " WHERE profit_drain_serial_id > $1"
    3690             :       " ORDER BY profit_drain_serial_id ASC;",
    3691             :       1),
    3692             :     /* For postgres_insert_records_by_table */
    3693           0 :     GNUNET_PQ_make_prepare (
    3694             :       "insert_into_table_denominations",
    3695             :       "INSERT INTO denominations"
    3696             :       "(denominations_serial"
    3697             :       ",denom_pub_hash"
    3698             :       ",denom_type"
    3699             :       ",age_mask"
    3700             :       ",denom_pub"
    3701             :       ",master_sig"
    3702             :       ",valid_from"
    3703             :       ",expire_withdraw"
    3704             :       ",expire_deposit"
    3705             :       ",expire_legal"
    3706             :       ",coin_val"
    3707             :       ",coin_frac"
    3708             :       ",fee_withdraw_val"
    3709             :       ",fee_withdraw_frac"
    3710             :       ",fee_deposit_val"
    3711             :       ",fee_deposit_frac"
    3712             :       ",fee_refresh_val"
    3713             :       ",fee_refresh_frac"
    3714             :       ",fee_refund_val"
    3715             :       ",fee_refund_frac"
    3716             :       ") VALUES "
    3717             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
    3718             :       " $11, $12, $13, $14, $15, $16, $17, $18, $19, $20);",
    3719             :       20),
    3720           0 :     GNUNET_PQ_make_prepare (
    3721             :       "insert_into_table_denomination_revocations",
    3722             :       "INSERT INTO denomination_revocations"
    3723             :       "(denom_revocations_serial_id"
    3724             :       ",master_sig"
    3725             :       ",denominations_serial"
    3726             :       ") VALUES "
    3727             :       "($1, $2, $3);",
    3728             :       3),
    3729           0 :     GNUNET_PQ_make_prepare (
    3730             :       "insert_into_table_wire_targets",
    3731             :       "INSERT INTO wire_targets"
    3732             :       "(wire_target_serial_id"
    3733             :       ",wire_target_h_payto"
    3734             :       ",payto_uri"
    3735             :       ") VALUES "
    3736             :       "($1, $2, $3);",
    3737             :       3),
    3738           0 :     GNUNET_PQ_make_prepare (
    3739             :       "insert_into_table_reserves",
    3740             :       "INSERT INTO reserves"
    3741             :       "(reserve_uuid"
    3742             :       ",reserve_pub"
    3743             :       ",expiration_date"
    3744             :       ",gc_date"
    3745             :       ") VALUES "
    3746             :       "($1, $2, $3, $4);",
    3747             :       4),
    3748           0 :     GNUNET_PQ_make_prepare (
    3749             :       "insert_into_table_reserves_in",
    3750             :       "INSERT INTO reserves_in"
    3751             :       "(reserve_in_serial_id"
    3752             :       ",wire_reference"
    3753             :       ",credit_val"
    3754             :       ",credit_frac"
    3755             :       ",wire_source_h_payto"
    3756             :       ",exchange_account_section"
    3757             :       ",execution_date"
    3758             :       ",reserve_pub"
    3759             :       ") VALUES "
    3760             :       "($1, $2, $3, $4, $5, $6, $7, $8);",
    3761             :       8),
    3762           0 :     GNUNET_PQ_make_prepare (
    3763             :       "insert_into_table_reserves_close",
    3764             :       "INSERT INTO reserves_close"
    3765             :       "(close_uuid"
    3766             :       ",execution_date"
    3767             :       ",wtid"
    3768             :       ",wire_target_h_payto"
    3769             :       ",amount_val"
    3770             :       ",amount_frac"
    3771             :       ",closing_fee_val"
    3772             :       ",closing_fee_frac"
    3773             :       ",reserve_pub"
    3774             :       ") VALUES "
    3775             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
    3776             :       9),
    3777           0 :     GNUNET_PQ_make_prepare (
    3778             :       "insert_into_table_reserves_out",
    3779             :       "INSERT INTO reserves_out"
    3780             :       "(reserve_out_serial_id"
    3781             :       ",h_blind_ev"
    3782             :       ",denominations_serial"
    3783             :       ",denom_sig"
    3784             :       ",reserve_uuid"
    3785             :       ",reserve_sig"
    3786             :       ",execution_date"
    3787             :       ",amount_with_fee_val"
    3788             :       ",amount_with_fee_frac"
    3789             :       ") VALUES "
    3790             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
    3791             :       9),
    3792           0 :     GNUNET_PQ_make_prepare (
    3793             :       "insert_into_table_auditors",
    3794             :       "INSERT INTO auditors"
    3795             :       "(auditor_uuid"
    3796             :       ",auditor_pub"
    3797             :       ",auditor_name"
    3798             :       ",auditor_url"
    3799             :       ",is_active"
    3800             :       ",last_change"
    3801             :       ") VALUES "
    3802             :       "($1, $2, $3, $4, $5, $6);",
    3803             :       6),
    3804           0 :     GNUNET_PQ_make_prepare (
    3805             :       "insert_into_table_auditor_denom_sigs",
    3806             :       "INSERT INTO auditor_denom_sigs"
    3807             :       "(auditor_denom_serial"
    3808             :       ",auditor_uuid"
    3809             :       ",denominations_serial"
    3810             :       ",auditor_sig"
    3811             :       ") VALUES "
    3812             :       "($1, $2, $3, $4);",
    3813             :       4),
    3814           0 :     GNUNET_PQ_make_prepare (
    3815             :       "insert_into_table_exchange_sign_keys",
    3816             :       "INSERT INTO exchange_sign_keys"
    3817             :       "(esk_serial"
    3818             :       ",exchange_pub"
    3819             :       ",master_sig"
    3820             :       ",valid_from"
    3821             :       ",expire_sign"
    3822             :       ",expire_legal"
    3823             :       ") VALUES "
    3824             :       "($1, $2, $3, $4, $5, $6);",
    3825             :       6),
    3826           0 :     GNUNET_PQ_make_prepare (
    3827             :       "insert_into_table_signkey_revocations",
    3828             :       "INSERT INTO signkey_revocations"
    3829             :       "(signkey_revocations_serial_id"
    3830             :       ",esk_serial"
    3831             :       ",master_sig"
    3832             :       ") VALUES "
    3833             :       "($1, $2, $3);",
    3834             :       3),
    3835           0 :     GNUNET_PQ_make_prepare (
    3836             :       "insert_into_table_known_coins",
    3837             :       "INSERT INTO known_coins"
    3838             :       "(known_coin_id"
    3839             :       ",coin_pub"
    3840             :       ",denom_sig"
    3841             :       ",denominations_serial"
    3842             :       ") VALUES "
    3843             :       "($1, $2, $3, $4);",
    3844             :       4),
    3845           0 :     GNUNET_PQ_make_prepare (
    3846             :       "insert_into_table_refresh_commitments",
    3847             :       "INSERT INTO refresh_commitments"
    3848             :       "(melt_serial_id"
    3849             :       ",rc"
    3850             :       ",old_coin_sig"
    3851             :       ",amount_with_fee_val"
    3852             :       ",amount_with_fee_frac"
    3853             :       ",noreveal_index"
    3854             :       ",old_coin_pub"
    3855             :       ") VALUES "
    3856             :       "($1, $2, $3, $4, $5, $6, $7);",
    3857             :       7),
    3858           0 :     GNUNET_PQ_make_prepare (
    3859             :       "insert_into_table_refresh_revealed_coins",
    3860             :       "INSERT INTO refresh_revealed_coins"
    3861             :       "(rrc_serial"
    3862             :       ",freshcoin_index"
    3863             :       ",link_sig"
    3864             :       ",coin_ev"
    3865             :       ",h_coin_ev"
    3866             :       ",ev_sig"
    3867             :       ",ewv"
    3868             :       ",denominations_serial"
    3869             :       ",melt_serial_id"
    3870             :       ") VALUES "
    3871             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
    3872             :       9),
    3873           0 :     GNUNET_PQ_make_prepare (
    3874             :       "insert_into_table_refresh_transfer_keys",
    3875             :       "INSERT INTO refresh_transfer_keys"
    3876             :       "(rtc_serial"
    3877             :       ",transfer_pub"
    3878             :       ",transfer_privs"
    3879             :       ",melt_serial_id"
    3880             :       ") VALUES "
    3881             :       "($1, $2, $3, $4);",
    3882             :       4),
    3883           0 :     GNUNET_PQ_make_prepare (
    3884             :       "insert_into_table_deposits",
    3885             :       "INSERT INTO deposits"
    3886             :       "(deposit_serial_id"
    3887             :       ",shard"
    3888             :       ",known_coin_id"
    3889             :       ",coin_pub"
    3890             :       ",amount_with_fee_val"
    3891             :       ",amount_with_fee_frac"
    3892             :       ",wallet_timestamp"
    3893             :       ",exchange_timestamp"
    3894             :       ",refund_deadline"
    3895             :       ",wire_deadline"
    3896             :       ",merchant_pub"
    3897             :       ",h_contract_terms"
    3898             :       ",coin_sig"
    3899             :       ",wire_salt"
    3900             :       ",wire_target_h_payto"
    3901             :       ",extension_blocked"
    3902             :       ",extension_details_serial_id"
    3903             :       ") VALUES "
    3904             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
    3905             :       " $11, $12, $13, $14, $15, $16, $17);",
    3906             :       17),
    3907           0 :     GNUNET_PQ_make_prepare (
    3908             :       "insert_into_table_refunds",
    3909             :       "INSERT INTO refunds"
    3910             :       "(refund_serial_id"
    3911             :       ",coin_pub"
    3912             :       ",merchant_sig"
    3913             :       ",rtransaction_id"
    3914             :       ",amount_with_fee_val"
    3915             :       ",amount_with_fee_frac"
    3916             :       ",deposit_serial_id"
    3917             :       ") VALUES "
    3918             :       "($1, $2, $3, $4, $5, $6, $7);",
    3919             :       7),
    3920           0 :     GNUNET_PQ_make_prepare (
    3921             :       "insert_into_table_aggregation_tracking",
    3922             :       "INSERT INTO aggregation_tracking"
    3923             :       "(aggregation_serial_id"
    3924             :       ",deposit_serial_id"
    3925             :       ",wtid_raw"
    3926             :       ") VALUES "
    3927             :       "($1, $2, $3);",
    3928             :       3),
    3929           0 :     GNUNET_PQ_make_prepare (
    3930             :       "insert_into_table_wire_fee",
    3931             :       "INSERT INTO wire_fee"
    3932             :       "(wire_fee_serial"
    3933             :       ",wire_method"
    3934             :       ",start_date"
    3935             :       ",end_date"
    3936             :       ",wire_fee_val"
    3937             :       ",wire_fee_frac"
    3938             :       ",closing_fee_val"
    3939             :       ",closing_fee_frac"
    3940             :       ",wad_fee_val"
    3941             :       ",wad_fee_frac"
    3942             :       ",master_sig"
    3943             :       ") VALUES "
    3944             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);",
    3945             :       11),
    3946           0 :     GNUNET_PQ_make_prepare (
    3947             :       "insert_into_table_global_fee",
    3948             :       "INSERT INTO global_fee"
    3949             :       "(global_fee_serial"
    3950             :       ",start_date"
    3951             :       ",end_date"
    3952             :       ",history_fee_val"
    3953             :       ",history_fee_frac"
    3954             :       ",kyc_fee_val"
    3955             :       ",kyc_fee_frac"
    3956             :       ",account_fee_val"
    3957             :       ",account_fee_frac"
    3958             :       ",purse_fee_val"
    3959             :       ",purse_fee_frac"
    3960             :       ",purse_timeout"
    3961             :       ",kyc_timeout"
    3962             :       ",history_expiration"
    3963             :       ",purse_account_limit"
    3964             :       ",master_sig"
    3965             :       ") VALUES "
    3966             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16);",
    3967             :       16),
    3968           0 :     GNUNET_PQ_make_prepare (
    3969             :       "insert_into_table_recoup",
    3970             :       "INSERT INTO recoup"
    3971             :       "(recoup_uuid"
    3972             :       ",coin_sig"
    3973             :       ",coin_blind"
    3974             :       ",amount_val"
    3975             :       ",amount_frac"
    3976             :       ",recoup_timestamp"
    3977             :       ",coin_pub"
    3978             :       ",reserve_out_serial_id"
    3979             :       ") VALUES "
    3980             :       "($1, $2, $3, $4, $5, $6, $7, $8);",
    3981             :       8),
    3982           0 :     GNUNET_PQ_make_prepare (
    3983             :       "insert_into_table_recoup_refresh",
    3984             :       "INSERT INTO recoup_refresh"
    3985             :       "(recoup_refresh_uuid"
    3986             :       ",coin_sig"
    3987             :       ",coin_blind"
    3988             :       ",amount_val"
    3989             :       ",amount_frac"
    3990             :       ",recoup_timestamp"
    3991             :       ",known_coin_id"
    3992             :       ",coin_pub"
    3993             :       ",rrc_serial"
    3994             :       ") VALUES "
    3995             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
    3996             :       9),
    3997           0 :     GNUNET_PQ_make_prepare (
    3998             :       "insert_into_table_extensions",
    3999             :       "INSERT INTO extensions"
    4000             :       "(extension_id"
    4001             :       ",name"
    4002             :       ",config"
    4003             :       ") VALUES "
    4004             :       "($1, $2, $3);",
    4005             :       3),
    4006           0 :     GNUNET_PQ_make_prepare (
    4007             :       "insert_into_table_extension_details",
    4008             :       "INSERT INTO extension_details"
    4009             :       "(extension_details_serial_id"
    4010             :       ",extension_options"
    4011             :       ") VALUES "
    4012             :       "($1, $2);",
    4013             :       2),
    4014             : 
    4015           0 :     GNUNET_PQ_make_prepare (
    4016             :       "insert_into_table_purse_requests",
    4017             :       "INSERT INTO purse_requests"
    4018             :       "(purse_requests_serial_id"
    4019             :       ",purse_pub"
    4020             :       ",merge_pub"
    4021             :       ",purse_creation"
    4022             :       ",purse_expiration"
    4023             :       ",h_contract_terms"
    4024             :       ",age_limit"
    4025             :       ",flags"
    4026             :       ",amount_with_fee_val"
    4027             :       ",amount_with_fee_frac"
    4028             :       ",purse_fee_val"
    4029             :       ",purse_fee_frac"
    4030             :       ",purse_sig"
    4031             :       ") VALUES "
    4032             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13);",
    4033             :       13),
    4034           0 :     GNUNET_PQ_make_prepare (
    4035             :       "insert_into_table_purse_refunds",
    4036             :       "INSERT INTO purse_refunds"
    4037             :       "(purse_refunds_serial_id"
    4038             :       ",purse_pub"
    4039             :       ") VALUES "
    4040             :       "($1, $2);",
    4041             :       2),
    4042           0 :     GNUNET_PQ_make_prepare (
    4043             :       "insert_into_table_purse_merges",
    4044             :       "INSERT INTO purse_merges"
    4045             :       "(purse_merge_request_serial_id"
    4046             :       ",partner_serial_id"
    4047             :       ",reserve_pub"
    4048             :       ",purse_pub"
    4049             :       ",merge_sig"
    4050             :       ",merge_timestamp"
    4051             :       ") VALUES "
    4052             :       "($1, $2, $3, $4, $5, $6);",
    4053             :       6),
    4054           0 :     GNUNET_PQ_make_prepare (
    4055             :       "insert_into_table_purse_deposits",
    4056             :       "INSERT INTO purse_deposits"
    4057             :       "(purse_deposit_serial_id"
    4058             :       ",partner_serial_id"
    4059             :       ",purse_pub"
    4060             :       ",coin_pub"
    4061             :       ",amount_with_fee_val"
    4062             :       ",amount_with_fee_frac"
    4063             :       ",coin_sig"
    4064             :       ") VALUES "
    4065             :       "($1, $2, $3, $4, $5, $6, $7);",
    4066             :       7),
    4067           0 :     GNUNET_PQ_make_prepare (
    4068             :       "insert_into_table_history_requests",
    4069             :       "INSERT INTO history_requests"
    4070             :       "(history_request_serial_id"
    4071             :       ",reserve_pub"
    4072             :       ",request_timestamp"
    4073             :       ",reserve_sig"
    4074             :       ",history_fee_val"
    4075             :       ",history_fee_frac"
    4076             :       ") VALUES "
    4077             :       "($1, $2, $3, $4, $5, $6);",
    4078             :       6),
    4079           0 :     GNUNET_PQ_make_prepare (
    4080             :       "insert_into_table_close_requests",
    4081             :       "INSERT INTO close_requests"
    4082             :       "(close_request_serial_id"
    4083             :       ",reserve_pub"
    4084             :       ",close_timestamp"
    4085             :       ",reserve_sig"
    4086             :       ",close_val"
    4087             :       ",close_frac"
    4088             :       ") VALUES "
    4089             :       "($1, $2, $3, $4, $5, $6);",
    4090             :       6),
    4091           0 :     GNUNET_PQ_make_prepare (
    4092             :       "insert_into_table_wads_out",
    4093             :       "INSERT INTO wads_out"
    4094             :       "(wad_out_serial_id"
    4095             :       ",wad_id"
    4096             :       ",partner_serial_id"
    4097             :       ",amount_val"
    4098             :       ",amount_frac"
    4099             :       ",execution_time"
    4100             :       ") VALUES "
    4101             :       "($1, $2, $3, $4, $5, $6);",
    4102             :       6),
    4103           0 :     GNUNET_PQ_make_prepare (
    4104             :       "insert_into_table_wad_out_entries",
    4105             :       "INSERT INTO wad_out_entries"
    4106             :       "(wad_out_entry_serial_id"
    4107             :       ",wad_out_serial_id"
    4108             :       ",reserve_pub"
    4109             :       ",purse_pub"
    4110             :       ",h_contract"
    4111             :       ",purse_expiration"
    4112             :       ",merge_timestamp"
    4113             :       ",amount_with_fee_val"
    4114             :       ",amount_with_fee_frac"
    4115             :       ",wad_fee_val"
    4116             :       ",wad_fee_frac"
    4117             :       ",deposit_fees_val"
    4118             :       ",deposit_fees_frac"
    4119             :       ",reserve_sig"
    4120             :       ",purse_sig"
    4121             :       ") VALUES "
    4122             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15);",
    4123             :       15),
    4124           0 :     GNUNET_PQ_make_prepare (
    4125             :       "insert_into_table_wads_in",
    4126             :       "INSERT INTO wads_in"
    4127             :       "(wad_in_serial_id"
    4128             :       ",wad_id"
    4129             :       ",origin_exchange_url"
    4130             :       ",amount_val"
    4131             :       ",amount_frac"
    4132             :       ",arrival_time"
    4133             :       ") VALUES "
    4134             :       "($1, $2, $3, $4, $5, $6);",
    4135             :       6),
    4136           0 :     GNUNET_PQ_make_prepare (
    4137             :       "insert_into_table_wad_in_entries",
    4138             :       "INSERT INTO wad_in_entries"
    4139             :       "(wad_in_entry_serial_id"
    4140             :       ",wad_in_serial_id"
    4141             :       ",reserve_pub"
    4142             :       ",purse_pub"
    4143             :       ",h_contract"
    4144             :       ",purse_expiration"
    4145             :       ",merge_timestamp"
    4146             :       ",amount_with_fee_val"
    4147             :       ",amount_with_fee_frac"
    4148             :       ",wad_fee_val"
    4149             :       ",wad_fee_frac"
    4150             :       ",deposit_fees_val"
    4151             :       ",deposit_fees_frac"
    4152             :       ",reserve_sig"
    4153             :       ",purse_sig"
    4154             :       ") VALUES "
    4155             :       "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15);",
    4156             :       15),
    4157           0 :     GNUNET_PQ_make_prepare (
    4158             :       "insert_into_table_profit_drains",
    4159             :       "INSERT INTO profit_drains"
    4160             :       "(profit_drain_serial_id"
    4161             :       ",wtid"
    4162             :       ",account_section"
    4163             :       ",payto_uri"
    4164             :       ",trigger_date"
    4165             :       ",amount_val"
    4166             :       ",amount_frac"
    4167             :       ",master_sig"
    4168             :       ") VALUES "
    4169             :       "($1, $2, $3, $4, $5, $6, $7, $8);",
    4170             :       8),
    4171             : 
    4172             :     /* Used in #postgres_begin_shard() */
    4173           0 :     GNUNET_PQ_make_prepare (
    4174             :       "get_open_shard",
    4175             :       "SELECT"
    4176             :       " start_row"
    4177             :       ",end_row"
    4178             :       " FROM work_shards"
    4179             :       " WHERE job_name=$1"
    4180             :       "   AND completed=FALSE"
    4181             :       "   AND last_attempt<$2"
    4182             :       " ORDER BY last_attempt ASC"
    4183             :       " LIMIT 1;",
    4184             :       2),
    4185             :     /* Used in #postgres_begin_revolving_shard() */
    4186           0 :     GNUNET_PQ_make_prepare (
    4187             :       "get_open_revolving_shard",
    4188             :       "SELECT"
    4189             :       " start_row"
    4190             :       ",end_row"
    4191             :       " FROM revolving_work_shards"
    4192             :       " WHERE job_name=$1"
    4193             :       "   AND active=FALSE"
    4194             :       " ORDER BY last_attempt ASC"
    4195             :       " LIMIT 1;",
    4196             :       2),
    4197             :     /* Used in #postgres_begin_shard() */
    4198           0 :     GNUNET_PQ_make_prepare (
    4199             :       "reclaim_shard",
    4200             :       "UPDATE work_shards"
    4201             :       " SET last_attempt=$2"
    4202             :       " WHERE job_name=$1"
    4203             :       "   AND start_row=$3"
    4204             :       "   AND end_row=$4",
    4205             :       4),
    4206             :     /* Used in #postgres_begin_revolving_shard() */
    4207           0 :     GNUNET_PQ_make_prepare (
    4208             :       "reclaim_revolving_shard",
    4209             :       "UPDATE revolving_work_shards"
    4210             :       " SET last_attempt=$2"
    4211             :       "    ,active=TRUE"
    4212             :       " WHERE job_name=$1"
    4213             :       "   AND start_row=$3"
    4214             :       "   AND end_row=$4",
    4215             :       4),
    4216             :     /* Used in #postgres_begin_shard() */
    4217           0 :     GNUNET_PQ_make_prepare (
    4218             :       "get_last_shard",
    4219             :       "SELECT"
    4220             :       " end_row"
    4221             :       " FROM work_shards"
    4222             :       " WHERE job_name=$1"
    4223             :       " ORDER BY end_row DESC"
    4224             :       " LIMIT 1;",
    4225             :       1),
    4226             :     /* Used in #postgres_begin_revolving_shard() */
    4227           0 :     GNUNET_PQ_make_prepare (
    4228             :       "get_last_revolving_shard",
    4229             :       "SELECT"
    4230             :       " end_row"
    4231             :       " FROM revolving_work_shards"
    4232             :       " WHERE job_name=$1"
    4233             :       " ORDER BY end_row DESC"
    4234             :       " LIMIT 1;",
    4235             :       1),
    4236             :     /* Used in #postgres_abort_shard() */
    4237           0 :     GNUNET_PQ_make_prepare (
    4238             :       "abort_shard",
    4239             :       "UPDATE work_shards"
    4240             :       "   SET last_attempt=0"
    4241             :       " WHERE job_name = $1 "
    4242             :       "    AND start_row = $2 "
    4243             :       "    AND end_row = $3;",
    4244             :       3),
    4245             :     /* Used in #postgres_begin_shard() */
    4246           0 :     GNUNET_PQ_make_prepare (
    4247             :       "claim_next_shard",
    4248             :       "INSERT INTO work_shards"
    4249             :       "(job_name"
    4250             :       ",last_attempt"
    4251             :       ",start_row"
    4252             :       ",end_row"
    4253             :       ") VALUES "
    4254             :       "($1, $2, $3, $4);",
    4255             :       4),
    4256             :     /* Used in #postgres_claim_revolving_shard() */
    4257           0 :     GNUNET_PQ_make_prepare (
    4258             :       "create_revolving_shard",
    4259             :       "INSERT INTO revolving_work_shards"
    4260             :       "(job_name"
    4261             :       ",last_attempt"
    4262             :       ",start_row"
    4263             :       ",end_row"
    4264             :       ",active"
    4265             :       ") VALUES "
    4266             :       "($1, $2, $3, $4, TRUE);",
    4267             :       4),
    4268             :     /* Used in #postgres_complete_shard() */
    4269           0 :     GNUNET_PQ_make_prepare (
    4270             :       "complete_shard",
    4271             :       "UPDATE work_shards"
    4272             :       " SET completed=TRUE"
    4273             :       " WHERE job_name=$1"
    4274             :       "   AND start_row=$2"
    4275             :       "   AND end_row=$3",
    4276             :       3),
    4277             :     /* Used in #postgres_complete_shard() */
    4278           0 :     GNUNET_PQ_make_prepare (
    4279             :       "release_revolving_shard",
    4280             :       "UPDATE revolving_work_shards"
    4281             :       " SET active=FALSE"
    4282             :       " WHERE job_name=$1"
    4283             :       "   AND start_row=$2"
    4284             :       "   AND end_row=$3",
    4285             :       3),
    4286             :     /* Used in #postgres_set_extension_config */
    4287           0 :     GNUNET_PQ_make_prepare (
    4288             :       "set_extension_config",
    4289             :       "INSERT INTO extensions (name, config) VALUES ($1, $2) "
    4290             :       "ON CONFLICT (name) "
    4291             :       "DO UPDATE SET config=$2",
    4292             :       2),
    4293             :     /* Used in #postgres_get_extension_config */
    4294           0 :     GNUNET_PQ_make_prepare (
    4295             :       "get_extension_config",
    4296             :       "SELECT "
    4297             :       " config "
    4298             :       "FROM extensions"
    4299             :       "   WHERE name=$1;",
    4300             :       1),
    4301             :     /* Used in #postgres_insert_contract() */
    4302           0 :     GNUNET_PQ_make_prepare (
    4303             :       "insert_contract",
    4304             :       "INSERT INTO contracts"
    4305             :       "  (purse_pub"
    4306             :       "  ,pub_ckey"
    4307             :       "  ,e_contract"
    4308             :       "  ,contract_sig"
    4309             :       "  ,purse_expiration"
    4310             :       "  ) SELECT "
    4311             :       "  $1, $2, $3, $4, purse_expiration"
    4312             :       "  FROM purse_requests"
    4313             :       "  WHERE purse_pub=$1"
    4314             :       "  ON CONFLICT DO NOTHING;",
    4315             :       4),
    4316             :     /* Used in #postgres_select_contract */
    4317           0 :     GNUNET_PQ_make_prepare (
    4318             :       "select_contract",
    4319             :       "SELECT "
    4320             :       " purse_pub"
    4321             :       ",e_contract"
    4322             :       ",contract_sig"
    4323             :       " FROM contracts"
    4324             :       "   WHERE pub_ckey=$1;",
    4325             :       1),
    4326             :     /* Used in #postgres_select_contract_by_purse */
    4327           0 :     GNUNET_PQ_make_prepare (
    4328             :       "select_contract_by_purse",
    4329             :       "SELECT "
    4330             :       " pub_ckey"
    4331             :       ",e_contract"
    4332             :       ",contract_sig"
    4333             :       " FROM contracts"
    4334             :       "   WHERE purse_pub=$1;",
    4335             :       1),
    4336             :     /* Used in #postgres_insert_purse_request() */
    4337           0 :     GNUNET_PQ_make_prepare (
    4338             :       "insert_purse_request",
    4339             :       "INSERT INTO purse_requests"
    4340             :       "  (purse_pub"
    4341             :       "  ,merge_pub"
    4342             :       "  ,purse_creation"
    4343             :       "  ,purse_expiration"
    4344             :       "  ,h_contract_terms"
    4345             :       "  ,age_limit"
    4346             :       "  ,flags"
    4347             :       "  ,in_reserve_quota"
    4348             :       "  ,amount_with_fee_val"
    4349             :       "  ,amount_with_fee_frac"
    4350             :       "  ,purse_fee_val"
    4351             :       "  ,purse_fee_frac"
    4352             :       "  ,purse_sig"
    4353             :       "  ) VALUES "
    4354             :       "  ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)"
    4355             :       "  ON CONFLICT DO NOTHING;",
    4356             :       13),
    4357             :     /* Used in #postgres_select_purse */
    4358           0 :     GNUNET_PQ_make_prepare (
    4359             :       "select_purse",
    4360             :       "SELECT "
    4361             :       " merge_pub"
    4362             :       ",purse_expiration"
    4363             :       ",h_contract_terms"
    4364             :       ",amount_with_fee_val"
    4365             :       ",amount_with_fee_frac"
    4366             :       ",balance_val"
    4367             :       ",balance_frac"
    4368             :       ",merge_timestamp"
    4369             :       " FROM purse_requests"
    4370             :       " LEFT JOIN purse_merges USING (purse_pub)"
    4371             :       " WHERE purse_pub=$1;",
    4372             :       1),
    4373             :     /* Used in #postgres_select_purse_request */
    4374           0 :     GNUNET_PQ_make_prepare (
    4375             :       "select_purse_request",
    4376             :       "SELECT "
    4377             :       " merge_pub"
    4378             :       ",purse_expiration"
    4379             :       ",h_contract_terms"
    4380             :       ",age_limit"
    4381             :       ",amount_with_fee_val"
    4382             :       ",amount_with_fee_frac"
    4383             :       ",balance_val"
    4384             :       ",balance_frac"
    4385             :       ",purse_sig"
    4386             :       " FROM purse_requests"
    4387             :       " WHERE purse_pub=$1;",
    4388             :       1),
    4389             :     /* Used in #postgres_select_purse_by_merge_pub */
    4390           0 :     GNUNET_PQ_make_prepare (
    4391             :       "select_purse_by_merge_pub",
    4392             :       "SELECT "
    4393             :       " purse_pub"
    4394             :       ",purse_expiration"
    4395             :       ",h_contract_terms"
    4396             :       ",age_limit"
    4397             :       ",amount_with_fee_val"
    4398             :       ",amount_with_fee_frac"
    4399             :       ",balance_val"
    4400             :       ",balance_frac"
    4401             :       ",purse_sig"
    4402             :       " FROM purse_requests"
    4403             :       " WHERE merge_pub=$1;",
    4404             :       1),
    4405             :     /* Used in #postgres_get_purse_deposit */
    4406           0 :     GNUNET_PQ_make_prepare (
    4407             :       "select_purse_deposit_by_coin_pub",
    4408             :       "SELECT "
    4409             :       " coin_sig"
    4410             :       ",amount_with_fee_val"
    4411             :       ",amount_with_fee_frac"
    4412             :       ",denom_pub_hash"
    4413             :       ",age_commitment_hash"
    4414             :       ",partner_base_url"
    4415             :       " FROM purse_deposits"
    4416             :       " LEFT JOIN partners USING (partner_serial_id)"
    4417             :       " JOIN known_coins kc USING (coin_pub)"
    4418             :       " JOIN denominations USING (denominations_serial)"
    4419             :       " WHERE coin_pub=$2"
    4420             :       "   AND purse_pub=$1;",
    4421             :       2),
    4422             :     /* Used in #postgres_do_purse_merge() */
    4423           0 :     GNUNET_PQ_make_prepare (
    4424             :       "call_purse_merge",
    4425             :       "SELECT"
    4426             :       " out_no_partner AS no_partner"
    4427             :       ",out_no_balance AS no_balance"
    4428             :       ",out_conflict AS conflict"
    4429             :       " FROM exchange_do_purse_merge"
    4430             :       "  ($1, $2, $3, $4, $5, $6, $7, $8);",
    4431             :       7),
    4432             :     /* Used in #postgres_do_reserve_purse() */
    4433           0 :     GNUNET_PQ_make_prepare (
    4434             :       "call_reserve_purse",
    4435             :       "SELECT"
    4436             :       " out_no_funds AS insufficient_funds"
    4437             :       ",out_no_reserve AS no_reserve"
    4438             :       ",out_conflict AS conflict"
    4439             :       " FROM exchange_do_reserve_purse"
    4440             :       "  ($1, $2, $3, $4, $5, $6, $7, $8, $9);",
    4441             :       9),
    4442             :     /* Used in #postgres_select_purse_merge */
    4443           0 :     GNUNET_PQ_make_prepare (
    4444             :       "select_purse_merge",
    4445             :       "SELECT "
    4446             :       " reserve_pub"
    4447             :       ",merge_sig"
    4448             :       ",merge_timestamp"
    4449             :       ",partner_base_url"
    4450             :       " FROM purse_merges"
    4451             :       " LEFT JOIN partners USING (partner_serial_id)"
    4452             :       " WHERE purse_pub=$1;",
    4453             :       1),
    4454             :     /* Used in #postgres_do_account_merge() */
    4455           0 :     GNUNET_PQ_make_prepare (
    4456             :       "call_account_merge",
    4457             :       "SELECT 1"
    4458             :       " FROM exchange_do_account_merge"
    4459             :       "  ($1, $2, $3);",
    4460             :       3),
    4461             :     /* Used in #postgres_insert_history_request() */
    4462           0 :     GNUNET_PQ_make_prepare (
    4463             :       "call_history_request",
    4464             :       "SELECT"
    4465             :       "  out_balance_ok AS balance_ok"
    4466             :       " ,out_idempotent AS idempotent"
    4467             :       " FROM exchange_do_history_request"
    4468             :       "  ($1, $2, $3, $4, $5)",
    4469             :       5),
    4470             :     /* Used in #postgres_insert_close_request() */
    4471           0 :     GNUNET_PQ_make_prepare (
    4472             :       "call_account_close",
    4473             :       "SELECT "
    4474             :       " out_final_balance_val"
    4475             :       ",out_final_balance_frac"
    4476             :       " FROM exchange_do_close_request"
    4477             :       "  ($1, $2, $3)",
    4478             :       3),
    4479             :     /* Used in #postgres_insert_kyc_requirement_for_account() */
    4480           0 :     GNUNET_PQ_make_prepare (
    4481             :       "insert_legitimization_requirement",
    4482             :       "INSERT INTO legitimization_requirements"
    4483             :       "  (h_payto"
    4484             :       "  ,required_checks"
    4485             :       "  ) VALUES "
    4486             :       "  ($1, $2)"
    4487             :       " ON CONFLICT (h_payto,required_checks) "
    4488             :       "   DO UPDATE SET h_payto=$1" /* syntax requirement: dummy op */
    4489             :       " RETURNING legitimization_requirement_serial_id",
    4490             :       2),
    4491             :     /* Used in #postgres_insert_kyc_requirement_process() */
    4492           0 :     GNUNET_PQ_make_prepare (
    4493             :       "insert_legitimization_process",
    4494             :       "INSERT INTO legitimization_processes"
    4495             :       "  (h_payto"
    4496             :       "  ,provider_section"
    4497             :       "  ,provider_user_id"
    4498             :       "  ,provider_legitimization_id"
    4499             :       "  ) VALUES "
    4500             :       "  ($1, $2, $3, $4)"
    4501             :       " ON CONFLICT (h_payto,provider_section) "
    4502             :       "   DO UPDATE SET"
    4503             :       "      provider_user_id=$3"
    4504             :       "     ,provider_legitimization_id=$4"
    4505             :       " RETURNING legitimization_process_serial_id",
    4506             :       4),
    4507             :     /* Used in #postgres_update_kyc_requirement_by_row() */
    4508           0 :     GNUNET_PQ_make_prepare (
    4509             :       "update_legitimization_process",
    4510             :       "UPDATE legitimization_processes"
    4511             :       " SET provider_user_id=$4"
    4512             :       "    ,provider_legitimization_id=$5"
    4513             :       "    ,expiration_time=GREATEST(expiration_time,$6)"
    4514             :       " WHERE"
    4515             :       "      h_payto=$3"
    4516             :       "  AND legitimization_process_serial_id=$1"
    4517             :       "  AND provider_section=$2;",
    4518             :       6),
    4519           0 :     GNUNET_PQ_make_prepare (
    4520             :       "alert_kyc_status_change",
    4521             :       "INSERT INTO kyc_alerts"
    4522             :       " (h_payto"
    4523             :       " ,trigger_type)"
    4524             :       " VALUES"
    4525             :       " ($1,$2);",
    4526             :       2),
    4527             :     /* Used in #postgres_lookup_kyc_requirement_by_row() */
    4528           0 :     GNUNET_PQ_make_prepare (
    4529             :       "lookup_legitimization_requirement_by_row",
    4530             :       "SELECT "
    4531             :       " required_checks"
    4532             :       ",h_payto"
    4533             :       " FROM legitimization_requirements"
    4534             :       " WHERE legitimization_requirement_serial_id=$1;",
    4535             :       1),
    4536             :     /* Used in #postgres_lookup_kyc_process_by_account() */
    4537           0 :     GNUNET_PQ_make_prepare (
    4538             :       "lookup_process_by_account",
    4539             :       "SELECT "
    4540             :       " legitimization_process_serial_id"
    4541             :       ",expiration_time"
    4542             :       ",provider_user_id"
    4543             :       ",provider_legitimization_id"
    4544             :       " FROM legitimization_processes"
    4545             :       " WHERE h_payto=$1"
    4546             :       "   AND provider_section=$2;",
    4547             :       2),
    4548             :     /* Used in #postgres_kyc_provider_account_lookup() */
    4549           0 :     GNUNET_PQ_make_prepare (
    4550             :       "get_wire_target_by_legitimization_id",
    4551             :       "SELECT "
    4552             :       " h_payto"
    4553             :       ",legitimization_process_serial_id"
    4554             :       " FROM legitimization_processes"
    4555             :       " WHERE provider_legitimization_id=$1"
    4556             :       "   AND provider_section=$2;",
    4557             :       2),
    4558             :     /* Used in #postgres_select_satisfied_kyc_processes() */
    4559           0 :     GNUNET_PQ_make_prepare (
    4560             :       "get_satisfied_legitimizations",
    4561             :       "SELECT "
    4562             :       " provider_section"
    4563             :       " FROM legitimization_processes"
    4564             :       " WHERE h_payto=$1"
    4565             :       "   AND expiration_time>=$2;",
    4566             :       2),
    4567             : 
    4568             :     /* Used in #postgres_select_withdraw_amounts_for_kyc_check (
    4569             : () */
    4570           0 :     GNUNET_PQ_make_prepare (
    4571             :       "select_kyc_relevant_withdraw_events",
    4572             :       "SELECT"
    4573             :       " ro.amount_with_fee_val AS amount_val"
    4574             :       ",ro.amount_with_fee_frac AS amount_frac"
    4575             :       ",ro.execution_date AS date"
    4576             :       " FROM reserves_out ro"
    4577             :       " JOIN reserves_out_by_reserve USING (h_blind_ev)"
    4578             :       " JOIN reserves res ON (ro.reserve_uuid = res.reserve_uuid)"
    4579             :       " JOIN reserves_in ri ON (res.reserve_pub = ri.reserve_pub)"
    4580             :       " WHERE wire_source_h_payto=$1"
    4581             :       "   AND ro.execution_date >= $2"
    4582             :       " ORDER BY ro.execution_date DESC",
    4583             :       2),
    4584             :     /* Used in #postgres_select_aggregation_amounts_for_kyc_check (
    4585             : () */
    4586           0 :     GNUNET_PQ_make_prepare (
    4587             :       "select_kyc_relevant_aggregation_events",
    4588             :       "SELECT"
    4589             :       " amount_val"
    4590             :       ",amount_frac"
    4591             :       ",execution_date AS date"
    4592             :       " FROM wire_out"
    4593             :       " WHERE wire_target_h_payto=$1"
    4594             :       "   AND execution_date >= $2"
    4595             :       " ORDER BY execution_date DESC",
    4596             :       2),
    4597             : 
    4598             :     /* Used in #postgres_select_merge_amounts_for_kyc_check (
    4599             : () */
    4600           0 :     GNUNET_PQ_make_prepare (
    4601             :       "select_kyc_relevant_merge_events",
    4602             :       "SELECT"
    4603             :       " amount_with_fee_val AS amount_val"
    4604             :       ",amount_with_fee_frac AS amount_frac"
    4605             :       ",merge_timestamp AS date"
    4606             :       " FROM account_merges"
    4607             :       " JOIN purse_merges USING (purse_pub)"
    4608             :       " JOIN purse_requests USING (purse_pub)"
    4609             :       " WHERE wallet_h_payto=$1"
    4610             :       "   AND merge_timestamp >= $2"
    4611             :       "   AND finished"
    4612             :       " ORDER BY merge_timestamp DESC",
    4613             :       2),
    4614             : 
    4615             :     GNUNET_PQ_PREPARED_STATEMENT_END
    4616             :   };
    4617             : 
    4618           0 :   ret = GNUNET_PQ_prepare_statements (pg->conn,
    4619             :                                       ps);
    4620           0 :   if (GNUNET_OK != ret)
    4621           0 :     return ret;
    4622           0 :   pg->init = true;
    4623           0 :   return GNUNET_OK;
    4624             : }
    4625             : 
    4626             : 
    4627             : /**
    4628             :  * Connect to the database if the connection does not exist yet.
    4629             :  *
    4630             :  * @param pg the plugin-specific state
    4631             :  * @param skip_prepare true if we should skip prepared statement setup
    4632             :  * @return #GNUNET_OK on success
    4633             :  */
    4634             : static enum GNUNET_GenericReturnValue
    4635          21 : internal_setup (struct PostgresClosure *pg,
    4636             :                 bool skip_prepare)
    4637             : {
    4638          21 :   if (NULL == pg->conn)
    4639             :   {
    4640             : #if AUTO_EXPLAIN
    4641             :     /* Enable verbose logging to see where queries do not
    4642             :        properly use indices */
    4643          21 :     struct GNUNET_PQ_ExecuteStatement es[] = {
    4644          21 :       GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"),
    4645          21 :       GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"),
    4646          21 :       GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"),
    4647          21 :       GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"),
    4648             :       /* https://wiki.postgresql.org/wiki/Serializable suggests to really
    4649             :          force the default to 'serializable' if SSI is to be used. */
    4650          21 :       GNUNET_PQ_make_try_execute (
    4651             :         "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
    4652          21 :       GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
    4653          21 :       GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
    4654          21 :       GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"),
    4655             :       GNUNET_PQ_EXECUTE_STATEMENT_END
    4656             :     };
    4657             : #else
    4658             :     struct GNUNET_PQ_ExecuteStatement es[] = {
    4659             :       GNUNET_PQ_make_try_execute (
    4660             :         "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
    4661             :       GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
    4662             :       GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
    4663             :       GNUNET_PQ_make_try_execute ("SET autocommit=OFF;"),
    4664             :       GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"),
    4665             :       GNUNET_PQ_EXECUTE_STATEMENT_END
    4666             :     };
    4667             : #endif
    4668             :     struct GNUNET_PQ_Context *db_conn;
    4669             : 
    4670          21 :     db_conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
    4671             :                                           "exchangedb-postgres",
    4672             :                                           NULL,
    4673             :                                           es,
    4674             :                                           NULL);
    4675          21 :     if (NULL == db_conn)
    4676          21 :       return GNUNET_SYSERR;
    4677           0 :     pg->conn = db_conn;
    4678             :   }
    4679           0 :   if (NULL == pg->transaction_name)
    4680           0 :     GNUNET_PQ_reconnect_if_down (pg->conn);
    4681           0 :   if (pg->init)
    4682           0 :     return GNUNET_OK;
    4683           0 :   if (skip_prepare)
    4684           0 :     return GNUNET_OK;
    4685           0 :   return prepare_statements (pg);
    4686             : }
    4687             : 
    4688             : 
    4689             : /**
    4690             :  * Do a pre-flight check that we are not in an uncommitted transaction.
    4691             :  * If we are, try to commit the previous transaction and output a warning.
    4692             :  * Does not return anything, as we will continue regardless of the outcome.
    4693             :  *
    4694             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    4695             :  * @return #GNUNET_OK if everything is fine
    4696             :  *         #GNUNET_NO if a transaction was rolled back
    4697             :  *         #GNUNET_SYSERR on hard errors
    4698             :  */
    4699             : static enum GNUNET_GenericReturnValue
    4700           0 : postgres_preflight (void *cls)
    4701             : {
    4702           0 :   struct PostgresClosure *pg = cls;
    4703           0 :   struct GNUNET_PQ_ExecuteStatement es[] = {
    4704           0 :     GNUNET_PQ_make_execute ("ROLLBACK"),
    4705             :     GNUNET_PQ_EXECUTE_STATEMENT_END
    4706             :   };
    4707             : 
    4708           0 :   if (! pg->init)
    4709             :   {
    4710           0 :     if (GNUNET_OK !=
    4711           0 :         internal_setup (pg,
    4712             :                         false))
    4713           0 :       return GNUNET_SYSERR;
    4714             :   }
    4715           0 :   if (NULL == pg->transaction_name)
    4716           0 :     return GNUNET_OK; /* all good */
    4717           0 :   if (GNUNET_OK ==
    4718           0 :       GNUNET_PQ_exec_statements (pg->conn,
    4719             :                                  es))
    4720             :   {
    4721           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4722             :                 "BUG: Preflight check rolled back transaction `%s'!\n",
    4723             :                 pg->transaction_name);
    4724             :   }
    4725             :   else
    4726             :   {
    4727           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4728             :                 "BUG: Preflight check failed to rollback transaction `%s'!\n",
    4729             :                 pg->transaction_name);
    4730             :   }
    4731           0 :   pg->transaction_name = NULL;
    4732           0 :   return GNUNET_NO;
    4733             : }
    4734             : 
    4735             : 
    4736             : /**
    4737             :  * Start a transaction.
    4738             :  *
    4739             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    4740             :  * @param name unique name identifying the transaction (for debugging)
    4741             :  *             must point to a constant
    4742             :  * @return #GNUNET_OK on success
    4743             :  */
    4744             : static enum GNUNET_GenericReturnValue
    4745           0 : postgres_start (void *cls,
    4746             :                 const char *name)
    4747             : {
    4748           0 :   struct PostgresClosure *pg = cls;
    4749           0 :   struct GNUNET_PQ_ExecuteStatement es[] = {
    4750           0 :     GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"),
    4751             :     GNUNET_PQ_EXECUTE_STATEMENT_END
    4752             :   };
    4753             : 
    4754           0 :   GNUNET_assert (NULL != name);
    4755           0 :   if (GNUNET_SYSERR ==
    4756           0 :       postgres_preflight (pg))
    4757           0 :     return GNUNET_SYSERR;
    4758           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    4759             :               "Starting transaction `%s'\n",
    4760             :               name);
    4761           0 :   if (GNUNET_OK !=
    4762           0 :       GNUNET_PQ_exec_statements (pg->conn,
    4763             :                                  es))
    4764             :   {
    4765           0 :     TALER_LOG_ERROR ("Failed to start transaction\n");
    4766           0 :     GNUNET_break (0);
    4767           0 :     return GNUNET_SYSERR;
    4768             :   }
    4769           0 :   pg->transaction_name = name;
    4770           0 :   return GNUNET_OK;
    4771             : }
    4772             : 
    4773             : 
    4774             : /**
    4775             :  * Start a READ COMMITTED transaction.
    4776             :  *
    4777             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    4778             :  * @param name unique name identifying the transaction (for debugging)
    4779             :  *             must point to a constant
    4780             :  * @return #GNUNET_OK on success
    4781             :  */
    4782             : static enum GNUNET_GenericReturnValue
    4783           0 : postgres_start_read_committed (void *cls,
    4784             :                                const char *name)
    4785             : {
    4786           0 :   struct PostgresClosure *pg = cls;
    4787           0 :   struct GNUNET_PQ_ExecuteStatement es[] = {
    4788           0 :     GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL READ COMMITTED"),
    4789             :     GNUNET_PQ_EXECUTE_STATEMENT_END
    4790             :   };
    4791             : 
    4792           0 :   GNUNET_assert (NULL != name);
    4793           0 :   if (GNUNET_SYSERR ==
    4794           0 :       postgres_preflight (pg))
    4795           0 :     return GNUNET_SYSERR;
    4796           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    4797             :               "Starting READ COMMITTED transaction `%s`\n",
    4798             :               name);
    4799           0 :   if (GNUNET_OK !=
    4800           0 :       GNUNET_PQ_exec_statements (pg->conn,
    4801             :                                  es))
    4802             :   {
    4803           0 :     TALER_LOG_ERROR ("Failed to start transaction\n");
    4804           0 :     GNUNET_break (0);
    4805           0 :     return GNUNET_SYSERR;
    4806             :   }
    4807           0 :   pg->transaction_name = name;
    4808           0 :   return GNUNET_OK;
    4809             : }
    4810             : 
    4811             : 
    4812             : /**
    4813             :  * Start a READ ONLY serializable transaction.
    4814             :  *
    4815             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    4816             :  * @param name unique name identifying the transaction (for debugging)
    4817             :  *             must point to a constant
    4818             :  * @return #GNUNET_OK on success
    4819             :  */
    4820             : static enum GNUNET_GenericReturnValue
    4821           0 : postgres_start_read_only (void *cls,
    4822             :                           const char *name)
    4823             : {
    4824           0 :   struct PostgresClosure *pg = cls;
    4825           0 :   struct GNUNET_PQ_ExecuteStatement es[] = {
    4826           0 :     GNUNET_PQ_make_execute (
    4827             :       "START TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY"),
    4828             :     GNUNET_PQ_EXECUTE_STATEMENT_END
    4829             :   };
    4830             : 
    4831           0 :   GNUNET_assert (NULL != name);
    4832           0 :   if (GNUNET_SYSERR ==
    4833           0 :       postgres_preflight (pg))
    4834           0 :     return GNUNET_SYSERR;
    4835           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    4836             :               "Starting READ ONLY transaction `%s`\n",
    4837             :               name);
    4838           0 :   if (GNUNET_OK !=
    4839           0 :       GNUNET_PQ_exec_statements (pg->conn,
    4840             :                                  es))
    4841             :   {
    4842           0 :     TALER_LOG_ERROR ("Failed to start transaction\n");
    4843           0 :     GNUNET_break (0);
    4844           0 :     return GNUNET_SYSERR;
    4845             :   }
    4846           0 :   pg->transaction_name = name;
    4847           0 :   return GNUNET_OK;
    4848             : }
    4849             : 
    4850             : 
    4851             : /**
    4852             :  * Roll back the current transaction of a database connection.
    4853             :  *
    4854             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    4855             :  */
    4856             : static void
    4857           0 : postgres_rollback (void *cls)
    4858             : {
    4859           0 :   struct PostgresClosure *pg = cls;
    4860           0 :   struct GNUNET_PQ_ExecuteStatement es[] = {
    4861           0 :     GNUNET_PQ_make_execute ("ROLLBACK"),
    4862             :     GNUNET_PQ_EXECUTE_STATEMENT_END
    4863             :   };
    4864             : 
    4865           0 :   if (NULL == pg->transaction_name)
    4866             :   {
    4867           0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    4868             :                 "Skipping rollback, no transaction active\n");
    4869           0 :     return;
    4870             :   }
    4871           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    4872             :               "Rolling back transaction\n");
    4873           0 :   GNUNET_break (GNUNET_OK ==
    4874             :                 GNUNET_PQ_exec_statements (pg->conn,
    4875             :                                            es));
    4876           0 :   pg->transaction_name = NULL;
    4877             : }
    4878             : 
    4879             : 
    4880             : /**
    4881             :  * Commit the current transaction of a database connection.
    4882             :  *
    4883             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    4884             :  * @return final transaction status
    4885             :  */
    4886             : static enum GNUNET_DB_QueryStatus
    4887           0 : postgres_commit (void *cls)
    4888             : {
    4889           0 :   struct PostgresClosure *pg = cls;
    4890           0 :   struct GNUNET_PQ_QueryParam params[] = {
    4891             :     GNUNET_PQ_query_param_end
    4892             :   };
    4893             :   enum GNUNET_DB_QueryStatus qs;
    4894             : 
    4895           0 :   GNUNET_break (NULL != pg->transaction_name);
    4896           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    4897             :               "Committing transaction `%s'\n",
    4898             :               pg->transaction_name);
    4899           0 :   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    4900             :                                            "do_commit",
    4901             :                                            params);
    4902           0 :   pg->transaction_name = NULL;
    4903           0 :   return qs;
    4904             : }
    4905             : 
    4906             : 
    4907             : /**
    4908             :  * Register callback to be invoked on events of type @a es.
    4909             :  *
    4910             :  * @param cls database context to use
    4911             :  * @param timeout how long until to generate a timeout event
    4912             :  * @param es specification of the event to listen for
    4913             :  * @param cb function to call when the event happens, possibly
    4914             :  *         multiple times (until cancel is invoked)
    4915             :  * @param cb_cls closure for @a cb
    4916             :  * @return handle useful to cancel the listener
    4917             :  */
    4918             : static struct GNUNET_DB_EventHandler *
    4919           0 : postgres_event_listen (void *cls,
    4920             :                        struct GNUNET_TIME_Relative timeout,
    4921             :                        const struct GNUNET_DB_EventHeaderP *es,
    4922             :                        GNUNET_DB_EventCallback cb,
    4923             :                        void *cb_cls)
    4924             : {
    4925           0 :   struct PostgresClosure *pg = cls;
    4926             : 
    4927           0 :   return GNUNET_PQ_event_listen (pg->conn,
    4928             :                                  es,
    4929             :                                  timeout,
    4930             :                                  cb,
    4931             :                                  cb_cls);
    4932             : }
    4933             : 
    4934             : 
    4935             : /**
    4936             :  * Stop notifications.
    4937             :  *
    4938             :  * @param cls the plugin's `struct PostgresClosure`
    4939             :  * @param eh handle to unregister.
    4940             :  */
    4941             : static void
    4942           0 : postgres_event_listen_cancel (void *cls,
    4943             :                               struct GNUNET_DB_EventHandler *eh)
    4944             : {
    4945             :   (void) cls;
    4946           0 :   GNUNET_PQ_event_listen_cancel (eh);
    4947           0 : }
    4948             : 
    4949             : 
    4950             : /**
    4951             :  * Notify all that listen on @a es of an event.
    4952             :  *
    4953             :  * @param cls database context to use
    4954             :  * @param es specification of the event to generate
    4955             :  * @param extra additional event data provided
    4956             :  * @param extra_size number of bytes in @a extra
    4957             :  */
    4958             : static void
    4959           0 : postgres_event_notify (void *cls,
    4960             :                        const struct GNUNET_DB_EventHeaderP *es,
    4961             :                        const void *extra,
    4962             :                        size_t extra_size)
    4963             : {
    4964           0 :   struct PostgresClosure *pg = cls;
    4965             : 
    4966           0 :   GNUNET_PQ_event_notify (pg->conn,
    4967             :                           es,
    4968             :                           extra,
    4969             :                           extra_size);
    4970           0 : }
    4971             : 
    4972             : 
    4973             : /**
    4974             :  * Insert a denomination key's public information into the database for
    4975             :  * reference by auditors and other consistency checks.
    4976             :  *
    4977             :  * @param cls the @e cls of this struct with the plugin-specific state
    4978             :  * @param denom_pub the public key used for signing coins of this denomination
    4979             :  * @param issue issuing information with value, fees and other info about the coin
    4980             :  * @return status of the query
    4981             :  */
    4982             : static enum GNUNET_DB_QueryStatus
    4983           0 : postgres_insert_denomination_info (
    4984             :   void *cls,
    4985             :   const struct TALER_DenominationPublicKey *denom_pub,
    4986             :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue)
    4987             : {
    4988           0 :   struct PostgresClosure *pg = cls;
    4989             :   struct TALER_DenominationHashP denom_hash;
    4990           0 :   struct GNUNET_PQ_QueryParam params[] = {
    4991           0 :     GNUNET_PQ_query_param_auto_from_type (&issue->denom_hash),
    4992           0 :     TALER_PQ_query_param_denom_pub (denom_pub),
    4993           0 :     GNUNET_PQ_query_param_auto_from_type (&issue->signature),
    4994           0 :     GNUNET_PQ_query_param_timestamp (&issue->start),
    4995           0 :     GNUNET_PQ_query_param_timestamp (&issue->expire_withdraw),
    4996           0 :     GNUNET_PQ_query_param_timestamp (&issue->expire_deposit),
    4997           0 :     GNUNET_PQ_query_param_timestamp (&issue->expire_legal),
    4998           0 :     TALER_PQ_query_param_amount (&issue->value),
    4999           0 :     TALER_PQ_query_param_amount (&issue->fees.withdraw),
    5000           0 :     TALER_PQ_query_param_amount (&issue->fees.deposit),
    5001           0 :     TALER_PQ_query_param_amount (&issue->fees.refresh),
    5002           0 :     TALER_PQ_query_param_amount (&issue->fees.refund),
    5003           0 :     GNUNET_PQ_query_param_uint32 (&denom_pub->age_mask.bits),
    5004             :     GNUNET_PQ_query_param_end
    5005             :   };
    5006             : 
    5007           0 :   GNUNET_assert (denom_pub->age_mask.bits ==
    5008             :                  issue->age_mask.bits);
    5009           0 :   TALER_denom_pub_hash (denom_pub,
    5010             :                         &denom_hash);
    5011           0 :   GNUNET_assert (0 ==
    5012             :                  GNUNET_memcmp (&denom_hash,
    5013             :                                 &issue->denom_hash));
    5014           0 :   GNUNET_assert (! GNUNET_TIME_absolute_is_zero (
    5015             :                    issue->start.abs_time));
    5016           0 :   GNUNET_assert (! GNUNET_TIME_absolute_is_zero (
    5017             :                    issue->expire_withdraw.abs_time));
    5018           0 :   GNUNET_assert (! GNUNET_TIME_absolute_is_zero (
    5019             :                    issue->expire_deposit.abs_time));
    5020           0 :   GNUNET_assert (! GNUNET_TIME_absolute_is_zero (
    5021             :                    issue->expire_legal.abs_time));
    5022             :   /* check fees match denomination currency */
    5023           0 :   GNUNET_assert (GNUNET_YES ==
    5024             :                  TALER_denom_fee_check_currency (
    5025             :                    issue->value.currency,
    5026             :                    &issue->fees));
    5027           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    5028             :                                              "denomination_insert",
    5029             :                                              params);
    5030             : }
    5031             : 
    5032             : 
    5033             : /**
    5034             :  * Fetch information about a denomination key.
    5035             :  *
    5036             :  * @param cls the @e cls of this struct with the plugin-specific state
    5037             :  * @param denom_pub_hash hash of the public key used for signing coins of this denomination
    5038             :  * @param[out] issue set to issue information with value, fees and other info about the coin
    5039             :  * @return transaction status code
    5040             :  */
    5041             : static enum GNUNET_DB_QueryStatus
    5042           0 : postgres_get_denomination_info (
    5043             :   void *cls,
    5044             :   const struct TALER_DenominationHashP *denom_pub_hash,
    5045             :   struct TALER_EXCHANGEDB_DenominationKeyInformation *issue)
    5046             : {
    5047           0 :   struct PostgresClosure *pg = cls;
    5048             :   enum GNUNET_DB_QueryStatus qs;
    5049           0 :   struct GNUNET_PQ_QueryParam params[] = {
    5050           0 :     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
    5051             :     GNUNET_PQ_query_param_end
    5052             :   };
    5053           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    5054           0 :     GNUNET_PQ_result_spec_auto_from_type ("master_sig",
    5055             :                                           &issue->signature),
    5056           0 :     GNUNET_PQ_result_spec_timestamp ("valid_from",
    5057             :                                      &issue->start),
    5058           0 :     GNUNET_PQ_result_spec_timestamp ("expire_withdraw",
    5059             :                                      &issue->expire_withdraw),
    5060           0 :     GNUNET_PQ_result_spec_timestamp ("expire_deposit",
    5061             :                                      &issue->expire_deposit),
    5062           0 :     GNUNET_PQ_result_spec_timestamp ("expire_legal",
    5063             :                                      &issue->expire_legal),
    5064           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("coin",
    5065             :                                  &issue->value),
    5066           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
    5067             :                                  &issue->fees.withdraw),
    5068           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    5069             :                                  &issue->fees.deposit),
    5070           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
    5071             :                                  &issue->fees.refresh),
    5072           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
    5073             :                                  &issue->fees.refund),
    5074           0 :     GNUNET_PQ_result_spec_uint32 ("age_mask",
    5075             :                                   &issue->age_mask.bits),
    5076             :     GNUNET_PQ_result_spec_end
    5077             :   };
    5078             : 
    5079           0 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    5080             :                                                  "denomination_get",
    5081             :                                                  params,
    5082             :                                                  rs);
    5083           0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    5084           0 :     return qs;
    5085           0 :   issue->denom_hash = *denom_pub_hash;
    5086           0 :   return qs;
    5087             : }
    5088             : 
    5089             : 
    5090             : /**
    5091             :  * Closure for #domination_cb_helper()
    5092             :  */
    5093             : struct DenomIteratorContext
    5094             : {
    5095             :   /**
    5096             :    * Function to call with the results.
    5097             :    */
    5098             :   TALER_EXCHANGEDB_DenominationCallback cb;
    5099             : 
    5100             :   /**
    5101             :    * Closure to pass to @e cb
    5102             :    */
    5103             :   void *cb_cls;
    5104             : 
    5105             :   /**
    5106             :    * Plugin context.
    5107             :    */
    5108             :   struct PostgresClosure *pg;
    5109             : };
    5110             : 
    5111             : 
    5112             : /**
    5113             :  * Helper function for #postgres_iterate_denomination_info().
    5114             :  * Calls the callback with each denomination key.
    5115             :  *
    5116             :  * @param cls a `struct DenomIteratorContext`
    5117             :  * @param result db results
    5118             :  * @param num_results number of results in @a result
    5119             :  */
    5120             : static void
    5121           0 : domination_cb_helper (void *cls,
    5122             :                       PGresult *result,
    5123             :                       unsigned int num_results)
    5124             : {
    5125           0 :   struct DenomIteratorContext *dic = cls;
    5126           0 :   struct PostgresClosure *pg = dic->pg;
    5127             : 
    5128           0 :   for (unsigned int i = 0; i<num_results; i++)
    5129             :   {
    5130             :     struct TALER_EXCHANGEDB_DenominationKeyInformation issue;
    5131             :     struct TALER_DenominationPublicKey denom_pub;
    5132             :     struct TALER_DenominationHashP denom_hash;
    5133           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5134           0 :       GNUNET_PQ_result_spec_auto_from_type ("master_sig",
    5135             :                                             &issue.signature),
    5136           0 :       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    5137             :                                             &denom_hash),
    5138           0 :       GNUNET_PQ_result_spec_timestamp ("valid_from",
    5139             :                                        &issue.start),
    5140           0 :       GNUNET_PQ_result_spec_timestamp ("expire_withdraw",
    5141             :                                        &issue.expire_withdraw),
    5142           0 :       GNUNET_PQ_result_spec_timestamp ("expire_deposit",
    5143             :                                        &issue.expire_deposit),
    5144           0 :       GNUNET_PQ_result_spec_timestamp ("expire_legal",
    5145             :                                        &issue.expire_legal),
    5146           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("coin",
    5147             :                                    &issue.value),
    5148           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
    5149             :                                    &issue.fees.withdraw),
    5150           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    5151             :                                    &issue.fees.deposit),
    5152           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
    5153             :                                    &issue.fees.refresh),
    5154           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
    5155             :                                    &issue.fees.refund),
    5156           0 :       TALER_PQ_result_spec_denom_pub ("denom_pub",
    5157             :                                       &denom_pub),
    5158           0 :       GNUNET_PQ_result_spec_uint32 ("age_mask",
    5159             :                                     &issue.age_mask.bits),
    5160             :       GNUNET_PQ_result_spec_end
    5161             :     };
    5162             : 
    5163           0 :     if (GNUNET_OK !=
    5164           0 :         GNUNET_PQ_extract_result (result,
    5165             :                                   rs,
    5166             :                                   i))
    5167             :     {
    5168           0 :       GNUNET_break (0);
    5169           0 :       return;
    5170             :     }
    5171             : 
    5172             :     /* Unfortunately we have to carry the age mask in both, the
    5173             :      * TALER_DenominationPublicKey and
    5174             :      * TALER_EXCHANGEDB_DenominationKeyInformation at different times.
    5175             :      * Here we use _both_ so let's make sure the values are the same. */
    5176           0 :     denom_pub.age_mask = issue.age_mask;
    5177           0 :     TALER_denom_pub_hash (&denom_pub,
    5178             :                           &issue.denom_hash);
    5179           0 :     if (0 !=
    5180           0 :         GNUNET_memcmp (&issue.denom_hash,
    5181             :                        &denom_hash))
    5182             :     {
    5183           0 :       GNUNET_break (0);
    5184             :     }
    5185             :     else
    5186             :     {
    5187           0 :       dic->cb (dic->cb_cls,
    5188             :                &denom_pub,
    5189             :                &issue);
    5190             :     }
    5191           0 :     TALER_denom_pub_free (&denom_pub);
    5192             :   }
    5193             : }
    5194             : 
    5195             : 
    5196             : /**
    5197             :  * Fetch information about all known denomination keys.
    5198             :  *
    5199             :  * @param cls the @e cls of this struct with the plugin-specific state
    5200             :  * @param cb function to call on each denomination key
    5201             :  * @param cb_cls closure for @a cb
    5202             :  * @return transaction status code
    5203             :  */
    5204             : static enum GNUNET_DB_QueryStatus
    5205           0 : postgres_iterate_denomination_info (void *cls,
    5206             :                                     TALER_EXCHANGEDB_DenominationCallback cb,
    5207             :                                     void *cb_cls)
    5208             : {
    5209           0 :   struct PostgresClosure *pg = cls;
    5210           0 :   struct GNUNET_PQ_QueryParam params[] = {
    5211             :     GNUNET_PQ_query_param_end
    5212             :   };
    5213           0 :   struct DenomIteratorContext dic = {
    5214             :     .cb = cb,
    5215             :     .cb_cls = cb_cls,
    5216             :     .pg = pg
    5217             :   };
    5218             : 
    5219           0 :   return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    5220             :                                                "denomination_iterate",
    5221             :                                                params,
    5222             :                                                &domination_cb_helper,
    5223             :                                                &dic);
    5224             : }
    5225             : 
    5226             : 
    5227             : /**
    5228             :  * Closure for #dominations_cb_helper()
    5229             :  */
    5230             : struct DenomsIteratorContext
    5231             : {
    5232             :   /**
    5233             :    * Function to call with the results.
    5234             :    */
    5235             :   TALER_EXCHANGEDB_DenominationsCallback cb;
    5236             : 
    5237             :   /**
    5238             :    * Closure to pass to @e cb
    5239             :    */
    5240             :   void *cb_cls;
    5241             : 
    5242             :   /**
    5243             :    * Plugin context.
    5244             :    */
    5245             :   struct PostgresClosure *pg;
    5246             : };
    5247             : 
    5248             : 
    5249             : /**
    5250             :  * Helper function for #postgres_iterate_denominations().
    5251             :  * Calls the callback with each denomination key.
    5252             :  *
    5253             :  * @param cls a `struct DenomsIteratorContext`
    5254             :  * @param result db results
    5255             :  * @param num_results number of results in @a result
    5256             :  */
    5257             : static void
    5258           0 : dominations_cb_helper (void *cls,
    5259             :                        PGresult *result,
    5260             :                        unsigned int num_results)
    5261             : {
    5262           0 :   struct DenomsIteratorContext *dic = cls;
    5263           0 :   struct PostgresClosure *pg = dic->pg;
    5264             : 
    5265           0 :   for (unsigned int i = 0; i<num_results; i++)
    5266             :   {
    5267           0 :     struct TALER_EXCHANGEDB_DenominationKeyMetaData meta = {0};
    5268           0 :     struct TALER_DenominationPublicKey denom_pub = {0};
    5269           0 :     struct TALER_MasterSignatureP master_sig = {0};
    5270           0 :     struct TALER_DenominationHashP h_denom_pub = {0};
    5271             :     bool revoked;
    5272           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5273           0 :       GNUNET_PQ_result_spec_auto_from_type ("master_sig",
    5274             :                                             &master_sig),
    5275           0 :       GNUNET_PQ_result_spec_bool ("revoked",
    5276             :                                   &revoked),
    5277           0 :       GNUNET_PQ_result_spec_timestamp ("valid_from",
    5278             :                                        &meta.start),
    5279           0 :       GNUNET_PQ_result_spec_timestamp ("expire_withdraw",
    5280             :                                        &meta.expire_withdraw),
    5281           0 :       GNUNET_PQ_result_spec_timestamp ("expire_deposit",
    5282             :                                        &meta.expire_deposit),
    5283           0 :       GNUNET_PQ_result_spec_timestamp ("expire_legal",
    5284             :                                        &meta.expire_legal),
    5285           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("coin",
    5286             :                                    &meta.value),
    5287           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
    5288             :                                    &meta.fees.withdraw),
    5289           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    5290             :                                    &meta.fees.deposit),
    5291           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
    5292             :                                    &meta.fees.refresh),
    5293           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
    5294             :                                    &meta.fees.refund),
    5295           0 :       TALER_PQ_result_spec_denom_pub ("denom_pub",
    5296             :                                       &denom_pub),
    5297           0 :       GNUNET_PQ_result_spec_uint32 ("age_mask",
    5298             :                                     &meta.age_mask.bits),
    5299             :       GNUNET_PQ_result_spec_end
    5300             :     };
    5301             : 
    5302           0 :     if (GNUNET_OK !=
    5303           0 :         GNUNET_PQ_extract_result (result,
    5304             :                                   rs,
    5305             :                                   i))
    5306             :     {
    5307           0 :       GNUNET_break (0);
    5308           0 :       return;
    5309             :     }
    5310             : 
    5311             :     /* make sure the mask information is the same */
    5312           0 :     denom_pub.age_mask = meta.age_mask;
    5313             : 
    5314           0 :     TALER_denom_pub_hash (&denom_pub,
    5315             :                           &h_denom_pub);
    5316           0 :     dic->cb (dic->cb_cls,
    5317             :              &denom_pub,
    5318             :              &h_denom_pub,
    5319             :              &meta,
    5320             :              &master_sig,
    5321             :              revoked);
    5322           0 :     GNUNET_PQ_cleanup_result (rs);
    5323             :   }
    5324             : }
    5325             : 
    5326             : 
    5327             : /**
    5328             :  * Function called to invoke @a cb on every known denomination key (revoked
    5329             :  * and non-revoked) that has been signed by the master key. Runs in its own
    5330             :  * read-only transaction.
    5331             :  *
    5332             :  *
    5333             :  * @param cls the @e cls of this struct with the plugin-specific state
    5334             :  * @param cb function to call on each denomination key
    5335             :  * @param cb_cls closure for @a cb
    5336             :  * @return transaction status code
    5337             :  */
    5338             : static enum GNUNET_DB_QueryStatus
    5339           0 : postgres_iterate_denominations (void *cls,
    5340             :                                 TALER_EXCHANGEDB_DenominationsCallback cb,
    5341             :                                 void *cb_cls)
    5342             : {
    5343           0 :   struct PostgresClosure *pg = cls;
    5344           0 :   struct GNUNET_PQ_QueryParam params[] = {
    5345             :     GNUNET_PQ_query_param_end
    5346             :   };
    5347           0 :   struct DenomsIteratorContext dic = {
    5348             :     .cb = cb,
    5349             :     .cb_cls = cb_cls,
    5350             :     .pg = pg
    5351             :   };
    5352             : 
    5353           0 :   return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    5354             :                                                "select_denominations",
    5355             :                                                params,
    5356             :                                                &dominations_cb_helper,
    5357             :                                                &dic);
    5358             : }
    5359             : 
    5360             : 
    5361             : /**
    5362             :  * Closure for #signkeys_cb_helper()
    5363             :  */
    5364             : struct SignkeysIteratorContext
    5365             : {
    5366             :   /**
    5367             :    * Function to call with the results.
    5368             :    */
    5369             :   TALER_EXCHANGEDB_ActiveSignkeysCallback cb;
    5370             : 
    5371             :   /**
    5372             :    * Closure to pass to @e cb
    5373             :    */
    5374             :   void *cb_cls;
    5375             : 
    5376             : };
    5377             : 
    5378             : 
    5379             : /**
    5380             :  * Helper function for #postgres_iterate_active_signkeys().
    5381             :  * Calls the callback with each signkey.
    5382             :  *
    5383             :  * @param cls a `struct SignkeysIteratorContext`
    5384             :  * @param result db results
    5385             :  * @param num_results number of results in @a result
    5386             :  */
    5387             : static void
    5388           0 : signkeys_cb_helper (void *cls,
    5389             :                     PGresult *result,
    5390             :                     unsigned int num_results)
    5391             : {
    5392           0 :   struct SignkeysIteratorContext *dic = cls;
    5393             : 
    5394           0 :   for (unsigned int i = 0; i<num_results; i++)
    5395             :   {
    5396             :     struct TALER_EXCHANGEDB_SignkeyMetaData meta;
    5397             :     struct TALER_ExchangePublicKeyP exchange_pub;
    5398             :     struct TALER_MasterSignatureP master_sig;
    5399           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5400           0 :       GNUNET_PQ_result_spec_auto_from_type ("master_sig",
    5401             :                                             &master_sig),
    5402           0 :       GNUNET_PQ_result_spec_auto_from_type ("exchange_pub",
    5403             :                                             &exchange_pub),
    5404           0 :       GNUNET_PQ_result_spec_timestamp ("valid_from",
    5405             :                                        &meta.start),
    5406           0 :       GNUNET_PQ_result_spec_timestamp ("expire_sign",
    5407             :                                        &meta.expire_sign),
    5408           0 :       GNUNET_PQ_result_spec_timestamp ("expire_legal",
    5409             :                                        &meta.expire_legal),
    5410             :       GNUNET_PQ_result_spec_end
    5411             :     };
    5412             : 
    5413           0 :     if (GNUNET_OK !=
    5414           0 :         GNUNET_PQ_extract_result (result,
    5415             :                                   rs,
    5416             :                                   i))
    5417             :     {
    5418           0 :       GNUNET_break (0);
    5419           0 :       return;
    5420             :     }
    5421           0 :     dic->cb (dic->cb_cls,
    5422             :              &exchange_pub,
    5423             :              &meta,
    5424             :              &master_sig);
    5425             :   }
    5426             : }
    5427             : 
    5428             : 
    5429             : /**
    5430             :  * Function called to invoke @a cb on every non-revoked exchange signing key
    5431             :  * that has been signed by the master key.  Revoked and (for signing!)
    5432             :  * expired keys are skipped. Runs in its own read-only transaction.
    5433             :  *
    5434             :  * @param cls the @e cls of this struct with the plugin-specific state
    5435             :  * @param cb function to call on each signing key
    5436             :  * @param cb_cls closure for @a cb
    5437             :  * @return transaction status code
    5438             :  */
    5439             : static enum GNUNET_DB_QueryStatus
    5440           0 : postgres_iterate_active_signkeys (void *cls,
    5441             :                                   TALER_EXCHANGEDB_ActiveSignkeysCallback cb,
    5442             :                                   void *cb_cls)
    5443             : {
    5444           0 :   struct PostgresClosure *pg = cls;
    5445           0 :   struct GNUNET_TIME_Absolute now = {0};
    5446           0 :   struct GNUNET_PQ_QueryParam params[] = {
    5447           0 :     GNUNET_PQ_query_param_absolute_time (&now),
    5448             :     GNUNET_PQ_query_param_end
    5449             :   };
    5450           0 :   struct SignkeysIteratorContext dic = {
    5451             :     .cb = cb,
    5452             :     .cb_cls = cb_cls,
    5453             :   };
    5454             : 
    5455           0 :   now = GNUNET_TIME_absolute_get ();
    5456           0 :   return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    5457             :                                                "select_signkeys",
    5458             :                                                params,
    5459             :                                                &signkeys_cb_helper,
    5460             :                                                &dic);
    5461             : }
    5462             : 
    5463             : 
    5464             : /**
    5465             :  * Closure for #auditors_cb_helper()
    5466             :  */
    5467             : struct AuditorsIteratorContext
    5468             : {
    5469             :   /**
    5470             :    * Function to call with the results.
    5471             :    */
    5472             :   TALER_EXCHANGEDB_AuditorsCallback cb;
    5473             : 
    5474             :   /**
    5475             :    * Closure to pass to @e cb
    5476             :    */
    5477             :   void *cb_cls;
    5478             : 
    5479             : };
    5480             : 
    5481             : 
    5482             : /**
    5483             :  * Helper function for #postgres_iterate_active_auditors().
    5484             :  * Calls the callback with each auditor.
    5485             :  *
    5486             :  * @param cls a `struct SignkeysIteratorContext`
    5487             :  * @param result db results
    5488             :  * @param num_results number of results in @a result
    5489             :  */
    5490             : static void
    5491           0 : auditors_cb_helper (void *cls,
    5492             :                     PGresult *result,
    5493             :                     unsigned int num_results)
    5494             : {
    5495           0 :   struct AuditorsIteratorContext *dic = cls;
    5496             : 
    5497           0 :   for (unsigned int i = 0; i<num_results; i++)
    5498             :   {
    5499             :     struct TALER_AuditorPublicKeyP auditor_pub;
    5500             :     char *auditor_url;
    5501             :     char *auditor_name;
    5502           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5503           0 :       GNUNET_PQ_result_spec_auto_from_type ("auditor_pub",
    5504             :                                             &auditor_pub),
    5505           0 :       GNUNET_PQ_result_spec_string ("auditor_url",
    5506             :                                     &auditor_url),
    5507           0 :       GNUNET_PQ_result_spec_string ("auditor_name",
    5508             :                                     &auditor_name),
    5509             :       GNUNET_PQ_result_spec_end
    5510             :     };
    5511             : 
    5512           0 :     if (GNUNET_OK !=
    5513           0 :         GNUNET_PQ_extract_result (result,
    5514             :                                   rs,
    5515             :                                   i))
    5516             :     {
    5517           0 :       GNUNET_break (0);
    5518           0 :       return;
    5519             :     }
    5520           0 :     dic->cb (dic->cb_cls,
    5521             :              &auditor_pub,
    5522             :              auditor_url,
    5523             :              auditor_name);
    5524           0 :     GNUNET_PQ_cleanup_result (rs);
    5525             :   }
    5526             : }
    5527             : 
    5528             : 
    5529             : /**
    5530             :  * Function called to invoke @a cb on every active auditor. Disabled
    5531             :  * auditors are skipped. Runs in its own read-only transaction.
    5532             :  *
    5533             :  * @param cls the @e cls of this struct with the plugin-specific state
    5534             :  * @param cb function to call on each active auditor
    5535             :  * @param cb_cls closure for @a cb
    5536             :  * @return transaction status code
    5537             :  */
    5538             : static enum GNUNET_DB_QueryStatus
    5539           0 : postgres_iterate_active_auditors (void *cls,
    5540             :                                   TALER_EXCHANGEDB_AuditorsCallback cb,
    5541             :                                   void *cb_cls)
    5542             : {
    5543           0 :   struct PostgresClosure *pg = cls;
    5544           0 :   struct GNUNET_PQ_QueryParam params[] = {
    5545             :     GNUNET_PQ_query_param_end
    5546             :   };
    5547           0 :   struct AuditorsIteratorContext dic = {
    5548             :     .cb = cb,
    5549             :     .cb_cls = cb_cls,
    5550             :   };
    5551             : 
    5552           0 :   return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    5553             :                                                "select_auditors",
    5554             :                                                params,
    5555             :                                                &auditors_cb_helper,
    5556             :                                                &dic);
    5557             : }
    5558             : 
    5559             : 
    5560             : /**
    5561             :  * Closure for #auditor_denoms_cb_helper()
    5562             :  */
    5563             : struct AuditorDenomsIteratorContext
    5564             : {
    5565             :   /**
    5566             :    * Function to call with the results.
    5567             :    */
    5568             :   TALER_EXCHANGEDB_AuditorDenominationsCallback cb;
    5569             : 
    5570             :   /**
    5571             :    * Closure to pass to @e cb
    5572             :    */
    5573             :   void *cb_cls;
    5574             : };
    5575             : 
    5576             : 
    5577             : /**
    5578             :  * Helper function for #postgres_iterate_auditor_denominations().
    5579             :  * Calls the callback with each auditor and denomination pair.
    5580             :  *
    5581             :  * @param cls a `struct AuditorDenomsIteratorContext`
    5582             :  * @param result db results
    5583             :  * @param num_results number of results in @a result
    5584             :  */
    5585             : static void
    5586           0 : auditor_denoms_cb_helper (void *cls,
    5587             :                           PGresult *result,
    5588             :                           unsigned int num_results)
    5589             : {
    5590           0 :   struct AuditorDenomsIteratorContext *dic = cls;
    5591             : 
    5592           0 :   for (unsigned int i = 0; i<num_results; i++)
    5593             :   {
    5594             :     struct TALER_AuditorPublicKeyP auditor_pub;
    5595             :     struct TALER_DenominationHashP h_denom_pub;
    5596             :     struct TALER_AuditorSignatureP auditor_sig;
    5597           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5598           0 :       GNUNET_PQ_result_spec_auto_from_type ("auditor_pub",
    5599             :                                             &auditor_pub),
    5600           0 :       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    5601             :                                             &h_denom_pub),
    5602           0 :       GNUNET_PQ_result_spec_auto_from_type ("auditor_sig",
    5603             :                                             &auditor_sig),
    5604             :       GNUNET_PQ_result_spec_end
    5605             :     };
    5606             : 
    5607           0 :     if (GNUNET_OK !=
    5608           0 :         GNUNET_PQ_extract_result (result,
    5609             :                                   rs,
    5610             :                                   i))
    5611             :     {
    5612           0 :       GNUNET_break (0);
    5613           0 :       return;
    5614             :     }
    5615           0 :     dic->cb (dic->cb_cls,
    5616             :              &auditor_pub,
    5617             :              &h_denom_pub,
    5618             :              &auditor_sig);
    5619             :   }
    5620             : }
    5621             : 
    5622             : 
    5623             : /**
    5624             :  * Function called to invoke @a cb on every denomination with an active
    5625             :  * auditor. Disabled auditors and denominations without auditor are
    5626             :  * skipped. Runs in its own read-only transaction.
    5627             :  *
    5628             :  * @param cls the @e cls of this struct with the plugin-specific state
    5629             :  * @param cb function to call on each active auditor
    5630             :  * @param cb_cls closure for @a cb
    5631             :  * @return transaction status code
    5632             :  */
    5633             : static enum GNUNET_DB_QueryStatus
    5634           0 : postgres_iterate_auditor_denominations (
    5635             :   void *cls,
    5636             :   TALER_EXCHANGEDB_AuditorDenominationsCallback cb,
    5637             :   void *cb_cls)
    5638             : {
    5639           0 :   struct PostgresClosure *pg = cls;
    5640           0 :   struct GNUNET_PQ_QueryParam params[] = {
    5641             :     GNUNET_PQ_query_param_end
    5642             :   };
    5643           0 :   struct AuditorDenomsIteratorContext dic = {
    5644             :     .cb = cb,
    5645             :     .cb_cls = cb_cls,
    5646             :   };
    5647             : 
    5648           0 :   return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    5649             :                                                "select_auditor_denoms",
    5650             :                                                params,
    5651             :                                                &auditor_denoms_cb_helper,
    5652             :                                                &dic);
    5653             : }
    5654             : 
    5655             : 
    5656             : /**
    5657             :  * Get the summary of a reserve.
    5658             :  *
    5659             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    5660             :  * @param[in,out] reserve the reserve data.  The public key of the reserve should be
    5661             :  *          set in this structure; it is used to query the database.  The balance
    5662             :  *          and expiration are then filled accordingly.
    5663             :  * @return transaction status
    5664             :  */
    5665             : static enum GNUNET_DB_QueryStatus
    5666           0 : postgres_reserves_get (void *cls,
    5667             :                        struct TALER_EXCHANGEDB_Reserve *reserve)
    5668             : {
    5669           0 :   struct PostgresClosure *pg = cls;
    5670           0 :   struct GNUNET_PQ_QueryParam params[] = {
    5671           0 :     GNUNET_PQ_query_param_auto_from_type (&reserve->pub),
    5672             :     GNUNET_PQ_query_param_end
    5673             :   };
    5674           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    5675           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
    5676             :                                  &reserve->balance),
    5677           0 :     GNUNET_PQ_result_spec_timestamp ("expiration_date",
    5678             :                                      &reserve->expiry),
    5679           0 :     GNUNET_PQ_result_spec_timestamp ("gc_date",
    5680             :                                      &reserve->gc),
    5681             :     GNUNET_PQ_result_spec_end
    5682             :   };
    5683             : 
    5684           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    5685             :                                                    "reserves_get",
    5686             :                                                    params,
    5687             :                                                    rs);
    5688             : }
    5689             : 
    5690             : 
    5691             : /**
    5692             :  * Get the origin of funds of a reserve.
    5693             :  *
    5694             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    5695             :  * @param reserve_pub public key of the reserve
    5696             :  * @param[out] h_payto set to hash of the wire source payto://-URI
    5697             :  * @return transaction status
    5698             :  */
    5699             : static enum GNUNET_DB_QueryStatus
    5700           0 : postgres_reserves_get_origin (
    5701             :   void *cls,
    5702             :   const struct TALER_ReservePublicKeyP *reserve_pub,
    5703             :   struct TALER_PaytoHashP *h_payto)
    5704             : {
    5705           0 :   struct PostgresClosure *pg = cls;
    5706           0 :   struct GNUNET_PQ_QueryParam params[] = {
    5707           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    5708             :     GNUNET_PQ_query_param_end
    5709             :   };
    5710           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    5711           0 :     GNUNET_PQ_result_spec_auto_from_type ("wire_source_h_payto",
    5712             :                                           h_payto),
    5713             :     GNUNET_PQ_result_spec_end
    5714             :   };
    5715             : 
    5716           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    5717             :                                                    "get_h_wire_source_of_reserve",
    5718             :                                                    params,
    5719             :                                                    rs);
    5720             : }
    5721             : 
    5722             : 
    5723             : /**
    5724             :  * Extract next KYC alert.  Deletes the alert.
    5725             :  *
    5726             :  * @param cls the @e cls of this struct with the plugin-specific state
    5727             :  * @param trigger_type which type of alert to drain
    5728             :  * @param[out] h_payto set to hash of payto-URI where KYC status changed
    5729             :  * @return transaction status
    5730             :  */
    5731             : static enum GNUNET_DB_QueryStatus
    5732           0 : postgres_drain_kyc_alert (void *cls,
    5733             :                           uint32_t trigger_type,
    5734             :                           struct TALER_PaytoHashP *h_payto)
    5735             : {
    5736           0 :   struct PostgresClosure *pg = cls;
    5737           0 :   struct GNUNET_PQ_QueryParam params[] = {
    5738           0 :     GNUNET_PQ_query_param_uint32 (&trigger_type),
    5739             :     GNUNET_PQ_query_param_end
    5740             :   };
    5741           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    5742           0 :     GNUNET_PQ_result_spec_auto_from_type ("h_payto",
    5743             :                                           h_payto),
    5744             :     GNUNET_PQ_result_spec_end
    5745             :   };
    5746             : 
    5747           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    5748             :                                                    "drain_kyc_alert",
    5749             :                                                    params,
    5750             :                                                    rs);
    5751             : }
    5752             : 
    5753             : 
    5754             : /**
    5755             :  * Updates a reserve with the data from the given reserve structure.
    5756             :  *
    5757             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    5758             :  * @param reserve the reserve structure whose data will be used to update the
    5759             :  *          corresponding record in the database.
    5760             :  * @return transaction status
    5761             :  */
    5762             : static enum GNUNET_DB_QueryStatus
    5763           0 : reserves_update (void *cls,
    5764             :                  const struct TALER_EXCHANGEDB_Reserve *reserve)
    5765             : {
    5766           0 :   struct PostgresClosure *pg = cls;
    5767           0 :   struct GNUNET_PQ_QueryParam params[] = {
    5768           0 :     GNUNET_PQ_query_param_timestamp (&reserve->expiry),
    5769           0 :     GNUNET_PQ_query_param_timestamp (&reserve->gc),
    5770           0 :     TALER_PQ_query_param_amount (&reserve->balance),
    5771           0 :     GNUNET_PQ_query_param_auto_from_type (&reserve->pub),
    5772             :     GNUNET_PQ_query_param_end
    5773             :   };
    5774             : 
    5775           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    5776             :                                              "reserve_update",
    5777             :                                              params);
    5778             : }
    5779             : 
    5780             : 
    5781             : /**
    5782             :  * Setup new wire target for @a payto_uri.
    5783             :  *
    5784             :  * @param pg the plugin-specific state
    5785             :  * @param payto_uri the payto URI to check
    5786             :  * @param[out] h_payto set to the hash of @a payto_uri
    5787             :  * @return transaction status
    5788             :  */
    5789             : static enum GNUNET_DB_QueryStatus
    5790           0 : setup_wire_target (
    5791             :   struct PostgresClosure *pg,
    5792             :   const char *payto_uri,
    5793             :   struct TALER_PaytoHashP *h_payto)
    5794             : {
    5795           0 :   struct GNUNET_PQ_QueryParam iparams[] = {
    5796           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
    5797           0 :     GNUNET_PQ_query_param_string (payto_uri),
    5798             :     GNUNET_PQ_query_param_end
    5799             :   };
    5800             : 
    5801           0 :   TALER_payto_hash (payto_uri,
    5802             :                     h_payto);
    5803           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    5804             :                                              "insert_kyc_status",
    5805             :                                              iparams);
    5806             : }
    5807             : 
    5808             : 
    5809             : /**
    5810             :  * Generate event notification for the reserve
    5811             :  * change.
    5812             :  *
    5813             :  * @param pg plugin state
    5814             :  * @param reserve_pub reserve to notfiy on
    5815             :  */
    5816             : static void
    5817           0 : notify_on_reserve (struct PostgresClosure *pg,
    5818             :                    const struct TALER_ReservePublicKeyP *reserve_pub)
    5819             : {
    5820           0 :   struct TALER_ReserveEventP rep = {
    5821           0 :     .header.size = htons (sizeof (rep)),
    5822           0 :     .header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
    5823             :     .reserve_pub = *reserve_pub
    5824             :   };
    5825             : 
    5826           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    5827             :               "Notifying on reserve!\n");
    5828           0 :   postgres_event_notify (pg,
    5829             :                          &rep.header,
    5830             :                          NULL,
    5831             :                          0);
    5832           0 : }
    5833             : 
    5834             : 
    5835             : /**
    5836             :  * Insert an incoming transaction into reserves.  New reserves are also
    5837             :  * created through this function. Started within the scope of an ongoing
    5838             :  * transaction.
    5839             :  *
    5840             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    5841             :  * @param reserve_pub public key of the reserve
    5842             :  * @param balance the amount that has to be added to the reserve
    5843             :  * @param execution_time when was the amount added
    5844             :  * @param sender_account_details account information for the sender (payto://-URL)
    5845             :  * @param exchange_account_section name of the section in the configuration for the exchange's
    5846             :  *                       account into which the deposit was made
    5847             :  * @param wire_ref unique reference identifying the wire transfer
    5848             :  * @return transaction status code
    5849             :  */
    5850             : static enum GNUNET_DB_QueryStatus
    5851           0 : postgres_reserves_in_insert (void *cls,
    5852             :                              const struct TALER_ReservePublicKeyP *reserve_pub,
    5853             :                              const struct TALER_Amount *balance,
    5854             :                              struct GNUNET_TIME_Timestamp execution_time,
    5855             :                              const char *sender_account_details,
    5856             :                              const char *exchange_account_section,
    5857             :                              uint64_t wire_ref)
    5858             : {
    5859           0 :   struct PostgresClosure *pg = cls;
    5860             :   enum GNUNET_DB_QueryStatus qs1;
    5861             :   struct TALER_EXCHANGEDB_Reserve reserve;
    5862             :   struct GNUNET_TIME_Timestamp expiry;
    5863             :   struct GNUNET_TIME_Timestamp gc;
    5864             :   uint64_t reserve_uuid;
    5865             : 
    5866           0 :   reserve.pub = *reserve_pub;
    5867           0 :   expiry = GNUNET_TIME_absolute_to_timestamp (
    5868             :     GNUNET_TIME_absolute_add (execution_time.abs_time,
    5869             :                               pg->idle_reserve_expiration_time));
    5870           0 :   gc = GNUNET_TIME_absolute_to_timestamp (
    5871             :     GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
    5872             :                               pg->legal_reserve_expiration_time));
    5873           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    5874             :               "Creating reserve %s with expiration in %s\n",
    5875             :               TALER_B2S (reserve_pub),
    5876             :               GNUNET_STRINGS_relative_time_to_string (
    5877             :                 pg->idle_reserve_expiration_time,
    5878             :                 GNUNET_NO));
    5879             :   /* Optimistically assume this is a new reserve, create balance for the first
    5880             :      time; we do this before adding the actual transaction to "reserves_in",
    5881             :      as for a new reserve it can't be a duplicate 'add' operation, and as
    5882             :      the 'add' operation needs the reserve entry as a foreign key. */
    5883             :   {
    5884           0 :     struct GNUNET_PQ_QueryParam params[] = {
    5885           0 :       GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    5886           0 :       TALER_PQ_query_param_amount (balance),
    5887           0 :       GNUNET_PQ_query_param_timestamp (&expiry),
    5888           0 :       GNUNET_PQ_query_param_timestamp (&gc),
    5889             :       GNUNET_PQ_query_param_end
    5890             :     };
    5891           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5892           0 :       GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
    5893             :                                     &reserve_uuid),
    5894             :       GNUNET_PQ_result_spec_end
    5895             :     };
    5896             : 
    5897           0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    5898             :                 "Reserve does not exist; creating a new one\n");
    5899             :     /* Note: query uses 'on conflict do nothing' */
    5900           0 :     qs1 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    5901             :                                                     "reserve_create",
    5902             :                                                     params,
    5903             :                                                     rs);
    5904           0 :     if (qs1 < 0)
    5905           0 :       return qs1;
    5906             :   }
    5907             : 
    5908             :   /* Create new incoming transaction, "ON CONFLICT DO NOTHING"
    5909             :      is again used to guard against duplicates. */
    5910             :   {
    5911             :     enum GNUNET_DB_QueryStatus qs2;
    5912             :     enum GNUNET_DB_QueryStatus qs3;
    5913             :     struct TALER_PaytoHashP h_payto;
    5914             : 
    5915           0 :     qs3 = setup_wire_target (pg,
    5916             :                              sender_account_details,
    5917             :                              &h_payto);
    5918           0 :     if (qs3 < 0)
    5919           0 :       return qs3;
    5920             :     /* We do not have the UUID, so insert by public key */
    5921           0 :     struct GNUNET_PQ_QueryParam params[] = {
    5922           0 :       GNUNET_PQ_query_param_auto_from_type (&reserve.pub),
    5923           0 :       GNUNET_PQ_query_param_uint64 (&wire_ref),
    5924           0 :       TALER_PQ_query_param_amount (balance),
    5925           0 :       GNUNET_PQ_query_param_string (exchange_account_section),
    5926           0 :       GNUNET_PQ_query_param_auto_from_type (&h_payto),
    5927           0 :       GNUNET_PQ_query_param_timestamp (&execution_time),
    5928             :       GNUNET_PQ_query_param_end
    5929             :     };
    5930             : 
    5931           0 :     qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    5932             :                                               "reserves_in_add_transaction",
    5933             :                                               params);
    5934             :     /* qs2 could be 0 as statement used 'ON CONFLICT DO NOTHING' */
    5935           0 :     if (0 >= qs2)
    5936             :     {
    5937           0 :       if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs2) &&
    5938             :            (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1) )
    5939             :       {
    5940             :         /* Conflict for the transaction, but the reserve was
    5941             :            just now created, that should be impossible. */
    5942           0 :         GNUNET_break (0); /* should be impossible: reserve was fresh,
    5943             :                              but transaction already known */
    5944           0 :         return GNUNET_DB_STATUS_HARD_ERROR;
    5945             :       }
    5946             :       /* Transaction was already known or error. We are finished. */
    5947           0 :       return qs2;
    5948             :     }
    5949             :   }
    5950           0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs1)
    5951             :   {
    5952             :     /* New reserve, we are finished */
    5953           0 :     notify_on_reserve (pg,
    5954             :                        reserve_pub);
    5955           0 :     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    5956             :   }
    5957             : 
    5958             :   /* we were wrong with our optimistic assumption:
    5959             :      reserve did already exist, need to do an update instead */
    5960             :   {
    5961             :     /* We need to move away from 'read committed' to serializable.
    5962             :        Also, we know that it should be safe to commit at this point.
    5963             :        (We are only run in a larger transaction for performance.) */
    5964             :     enum GNUNET_DB_QueryStatus cs;
    5965             : 
    5966           0 :     cs = postgres_commit (pg);
    5967           0 :     if (cs < 0)
    5968           0 :       return cs;
    5969           0 :     if (GNUNET_OK !=
    5970           0 :         postgres_start (pg,
    5971             :                         "reserve-update-serializable"))
    5972             :     {
    5973           0 :       GNUNET_break (0);
    5974           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    5975             :     }
    5976             :   }
    5977             :   {
    5978             :     enum GNUNET_DB_QueryStatus reserve_exists;
    5979             : 
    5980           0 :     reserve_exists = postgres_reserves_get (pg,
    5981             :                                             &reserve);
    5982           0 :     switch (reserve_exists)
    5983             :     {
    5984           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    5985           0 :       GNUNET_break (0);
    5986           0 :       return reserve_exists;
    5987           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    5988           0 :       return reserve_exists;
    5989           0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    5990             :       /* First we got a conflict, but then we cannot select? Very strange. */
    5991           0 :       GNUNET_break (0);
    5992           0 :       return GNUNET_DB_STATUS_SOFT_ERROR;
    5993           0 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    5994             :       /* continued below */
    5995           0 :       break;
    5996             :     }
    5997           0 :   }
    5998             : 
    5999             :   {
    6000             :     struct TALER_EXCHANGEDB_Reserve updated_reserve;
    6001             :     enum GNUNET_DB_QueryStatus qs3;
    6002             : 
    6003             :     /* If the reserve already existed, we need to still update the
    6004             :        balance; we do this after checking for duplication, as
    6005             :        otherwise we might have to actually pay the cost to roll this
    6006             :        back for duplicate transactions; like this, we should virtually
    6007             :        never actually have to rollback anything. */
    6008           0 :     updated_reserve.pub = reserve.pub;
    6009           0 :     if (0 >
    6010           0 :         TALER_amount_add (&updated_reserve.balance,
    6011             :                           &reserve.balance,
    6012             :                           balance))
    6013             :     {
    6014             :       /* currency overflow or incompatible currency */
    6015           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    6016             :                   "Attempt to deposit incompatible amount into reserve\n");
    6017           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    6018             :     }
    6019           0 :     updated_reserve.expiry = GNUNET_TIME_timestamp_max (expiry,
    6020             :                                                         reserve.expiry);
    6021           0 :     updated_reserve.gc = GNUNET_TIME_timestamp_max (gc,
    6022             :                                                     reserve.gc);
    6023           0 :     qs3 = reserves_update (pg,
    6024             :                            &updated_reserve);
    6025           0 :     switch (qs3)
    6026             :     {
    6027           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    6028           0 :       GNUNET_break (0);
    6029           0 :       return qs3;
    6030           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    6031           0 :       return qs3;
    6032           0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    6033             :       /* How can the UPDATE not work here? Very strange. */
    6034           0 :       GNUNET_break (0);
    6035           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    6036           0 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    6037             :       /* continued below */
    6038           0 :       break;
    6039             :     }
    6040           0 :   }
    6041           0 :   notify_on_reserve (pg,
    6042             :                      reserve_pub);
    6043             :   /* Go back to original transaction mode */
    6044             :   {
    6045             :     enum GNUNET_DB_QueryStatus cs;
    6046             : 
    6047           0 :     cs = postgres_commit (pg);
    6048           0 :     if (cs < 0)
    6049           0 :       return cs;
    6050           0 :     if (GNUNET_OK !=
    6051           0 :         postgres_start_read_committed (pg,
    6052             :                                        "reserve-insert-continued"))
    6053             :     {
    6054           0 :       GNUNET_break (0);
    6055           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    6056             :     }
    6057             :   }
    6058           0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    6059             : }
    6060             : 
    6061             : 
    6062             : /**
    6063             :  * Locate the response for a /reserve/withdraw request under the
    6064             :  * key of the hash of the blinded message.
    6065             :  *
    6066             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    6067             :  * @param bch hash that uniquely identifies the withdraw operation
    6068             :  * @param collectable corresponding collectable coin (blind signature)
    6069             :  *                    if a coin is found
    6070             :  * @return statement execution status
    6071             :  */
    6072             : static enum GNUNET_DB_QueryStatus
    6073           0 : postgres_get_withdraw_info (
    6074             :   void *cls,
    6075             :   const struct TALER_BlindedCoinHashP *bch,
    6076             :   struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
    6077             : {
    6078           0 :   struct PostgresClosure *pg = cls;
    6079           0 :   struct GNUNET_PQ_QueryParam params[] = {
    6080           0 :     GNUNET_PQ_query_param_auto_from_type (bch),
    6081             :     GNUNET_PQ_query_param_end
    6082             :   };
    6083           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6084           0 :     GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    6085             :                                           &collectable->denom_pub_hash),
    6086           0 :     TALER_PQ_result_spec_blinded_denom_sig ("denom_sig",
    6087             :                                             &collectable->sig),
    6088           0 :     GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
    6089             :                                           &collectable->reserve_sig),
    6090           0 :     GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    6091             :                                           &collectable->reserve_pub),
    6092           0 :     GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
    6093             :                                           &collectable->h_coin_envelope),
    6094           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    6095             :                                  &collectable->amount_with_fee),
    6096           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
    6097             :                                  &collectable->withdraw_fee),
    6098             :     GNUNET_PQ_result_spec_end
    6099             :   };
    6100             : 
    6101           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6102             :                                                    "get_withdraw_info",
    6103             :                                                    params,
    6104             :                                                    rs);
    6105             : }
    6106             : 
    6107             : 
    6108             : /**
    6109             :  * Perform withdraw operation, checking for sufficient balance
    6110             :  * and possibly persisting the withdrawal details.
    6111             :  *
    6112             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    6113             :  * @param nonce client-contributed input for CS denominations that must be checked for idempotency, or NULL for non-CS withdrawals
    6114             :  * @param[in,out] collectable corresponding collectable coin (blind signature) if a coin is found; possibly updated if a (different) signature exists already
    6115             :  * @param now current time (rounded)
    6116             :  * @param[out] found set to true if the reserve was found
    6117             :  * @param[out] balance_ok set to true if the balance was sufficient
    6118             :  * @param[out] nonce_ok set to false if the nonce was reused
    6119             :  * @param[out] ruuid set to the reserve's UUID (reserves table row)
    6120             :  * @return query execution status
    6121             :  */
    6122             : static enum GNUNET_DB_QueryStatus
    6123           0 : postgres_do_withdraw (
    6124             :   void *cls,
    6125             :   const struct TALER_CsNonce *nonce,
    6126             :   const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
    6127             :   struct GNUNET_TIME_Timestamp now,
    6128             :   bool *found,
    6129             :   bool *balance_ok,
    6130             :   bool *nonce_ok,
    6131             :   uint64_t *ruuid)
    6132             : {
    6133           0 :   struct PostgresClosure *pg = cls;
    6134             :   struct GNUNET_TIME_Timestamp gc;
    6135           0 :   struct GNUNET_PQ_QueryParam params[] = {
    6136             :     NULL == nonce
    6137           0 :     ? GNUNET_PQ_query_param_null ()
    6138           0 :     : GNUNET_PQ_query_param_auto_from_type (nonce),
    6139           0 :     TALER_PQ_query_param_amount (&collectable->amount_with_fee),
    6140           0 :     GNUNET_PQ_query_param_auto_from_type (&collectable->denom_pub_hash),
    6141           0 :     GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_pub),
    6142           0 :     GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_sig),
    6143           0 :     GNUNET_PQ_query_param_auto_from_type (&collectable->h_coin_envelope),
    6144           0 :     TALER_PQ_query_param_blinded_denom_sig (&collectable->sig),
    6145           0 :     GNUNET_PQ_query_param_timestamp (&now),
    6146           0 :     GNUNET_PQ_query_param_timestamp (&gc),
    6147             :     GNUNET_PQ_query_param_end
    6148             :   };
    6149           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6150           0 :     GNUNET_PQ_result_spec_bool ("reserve_found",
    6151             :                                 found),
    6152           0 :     GNUNET_PQ_result_spec_bool ("balance_ok",
    6153             :                                 balance_ok),
    6154           0 :     GNUNET_PQ_result_spec_bool ("nonce_ok",
    6155             :                                 nonce_ok),
    6156           0 :     GNUNET_PQ_result_spec_uint64 ("ruuid",
    6157             :                                   ruuid),
    6158             :     GNUNET_PQ_result_spec_end
    6159             :   };
    6160             : 
    6161           0 :   gc = GNUNET_TIME_absolute_to_timestamp (
    6162             :     GNUNET_TIME_absolute_add (now.abs_time,
    6163             :                               pg->legal_reserve_expiration_time));
    6164           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6165             :                                                    "call_withdraw",
    6166             :                                                    params,
    6167             :                                                    rs);
    6168             : }
    6169             : 
    6170             : 
    6171             : /**
    6172             :  * Perform reserve update as part of a batch withdraw operation, checking
    6173             :  * for sufficient balance. Persisting the withdrawal details is done
    6174             :  * separately!
    6175             :  *
    6176             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    6177             :  * @param now current time (rounded)
    6178             :  * @param reserve_pub public key of the reserve to debit
    6179             :  * @param amount total amount to withdraw
    6180             :  * @param[out] found set to true if the reserve was found
    6181             :  * @param[out] balance_ok set to true if the balance was sufficient
    6182             :  * @param[out] ruuid set to the reserve's UUID (reserves table row)
    6183             :  * @return query execution status
    6184             :  */
    6185             : static enum GNUNET_DB_QueryStatus
    6186           0 : postgres_do_batch_withdraw (
    6187             :   void *cls,
    6188             :   struct GNUNET_TIME_Timestamp now,
    6189             :   const struct TALER_ReservePublicKeyP *reserve_pub,
    6190             :   const struct TALER_Amount *amount,
    6191             :   bool *found,
    6192             :   bool *balance_ok,
    6193             :   uint64_t *ruuid)
    6194             : {
    6195           0 :   struct PostgresClosure *pg = cls;
    6196             :   struct GNUNET_TIME_Timestamp gc;
    6197           0 :   struct GNUNET_PQ_QueryParam params[] = {
    6198           0 :     TALER_PQ_query_param_amount (amount),
    6199           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    6200           0 :     GNUNET_PQ_query_param_timestamp (&now),
    6201           0 :     GNUNET_PQ_query_param_timestamp (&gc),
    6202             :     GNUNET_PQ_query_param_end
    6203             :   };
    6204           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6205           0 :     GNUNET_PQ_result_spec_bool ("reserve_found",
    6206             :                                 found),
    6207           0 :     GNUNET_PQ_result_spec_bool ("balance_ok",
    6208             :                                 balance_ok),
    6209           0 :     GNUNET_PQ_result_spec_uint64 ("ruuid",
    6210             :                                   ruuid),
    6211             :     GNUNET_PQ_result_spec_end
    6212             :   };
    6213             : 
    6214           0 :   gc = GNUNET_TIME_absolute_to_timestamp (
    6215             :     GNUNET_TIME_absolute_add (now.abs_time,
    6216             :                               pg->legal_reserve_expiration_time));
    6217           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6218             :                                                    "call_batch_withdraw",
    6219             :                                                    params,
    6220             :                                                    rs);
    6221             : }
    6222             : 
    6223             : 
    6224             : /**
    6225             :  * Perform insert as part of a batch withdraw operation, and persisting the
    6226             :  * withdrawal details.
    6227             :  *
    6228             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    6229             :  * @param nonce client-contributed input for CS denominations that must be checked for idempotency, or NULL for non-CS withdrawals
    6230             :  * @param collectable corresponding collectable coin (blind signature)
    6231             :  * @param now current time (rounded)
    6232             :  * @param ruuid reserve UUID
    6233             :  * @param[out] denom_unknown set if the denomination is unknown in the DB
    6234             :  * @param[out] conflict if the envelope was already in the DB
    6235             :  * @param[out] nonce_reuse if @a nonce was non-NULL and reused
    6236             :  * @return query execution status
    6237             :  */
    6238             : static enum GNUNET_DB_QueryStatus
    6239           0 : postgres_do_batch_withdraw_insert (
    6240             :   void *cls,
    6241             :   const struct TALER_CsNonce *nonce,
    6242             :   const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
    6243             :   struct GNUNET_TIME_Timestamp now,
    6244             :   uint64_t ruuid,
    6245             :   bool *denom_unknown,
    6246             :   bool *conflict,
    6247             :   bool *nonce_reuse)
    6248             : {
    6249           0 :   struct PostgresClosure *pg = cls;
    6250           0 :   struct GNUNET_PQ_QueryParam params[] = {
    6251             :     NULL == nonce
    6252           0 :     ? GNUNET_PQ_query_param_null ()
    6253           0 :     : GNUNET_PQ_query_param_auto_from_type (nonce),
    6254           0 :     TALER_PQ_query_param_amount (&collectable->amount_with_fee),
    6255           0 :     GNUNET_PQ_query_param_auto_from_type (&collectable->denom_pub_hash),
    6256           0 :     GNUNET_PQ_query_param_uint64 (&ruuid),
    6257           0 :     GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_sig),
    6258           0 :     GNUNET_PQ_query_param_auto_from_type (&collectable->h_coin_envelope),
    6259           0 :     TALER_PQ_query_param_blinded_denom_sig (&collectable->sig),
    6260           0 :     GNUNET_PQ_query_param_timestamp (&now),
    6261             :     GNUNET_PQ_query_param_end
    6262             :   };
    6263           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6264           0 :     GNUNET_PQ_result_spec_bool ("denom_unknown",
    6265             :                                 denom_unknown),
    6266           0 :     GNUNET_PQ_result_spec_bool ("conflict",
    6267             :                                 conflict),
    6268           0 :     GNUNET_PQ_result_spec_bool ("nonce_reuse",
    6269             :                                 nonce_reuse),
    6270             :     GNUNET_PQ_result_spec_end
    6271             :   };
    6272             : 
    6273           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6274             :                                                    "call_batch_withdraw_insert",
    6275             :                                                    params,
    6276             :                                                    rs);
    6277             : }
    6278             : 
    6279             : 
    6280             : /**
    6281             :  * Compute the shard number of a given @a merchant_pub.
    6282             :  *
    6283             :  * @param merchant_pub merchant public key to compute shard for
    6284             :  * @return shard number
    6285             :  */
    6286             : static uint64_t
    6287           0 : compute_shard (const struct TALER_MerchantPublicKeyP *merchant_pub)
    6288             : {
    6289             :   uint32_t res;
    6290             : 
    6291           0 :   GNUNET_assert (GNUNET_YES ==
    6292             :                  GNUNET_CRYPTO_kdf (&res,
    6293             :                                     sizeof (res),
    6294             :                                     merchant_pub,
    6295             :                                     sizeof (*merchant_pub),
    6296             :                                     "VOID",
    6297             :                                     4,
    6298             :                                     NULL, 0));
    6299             :   /* interpret hash result as NBO for platform independence,
    6300             :      convert to HBO and map to [0..2^31-1] range */
    6301           0 :   res = ntohl (res);
    6302           0 :   if (res > INT32_MAX)
    6303           0 :     res += INT32_MIN;
    6304           0 :   GNUNET_assert (res <= INT32_MAX);
    6305           0 :   return (uint64_t) res;
    6306             : }
    6307             : 
    6308             : 
    6309             : /**
    6310             :  * Perform deposit operation, checking for sufficient balance
    6311             :  * of the coin and possibly persisting the deposit details.
    6312             :  *
    6313             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    6314             :  * @param deposit deposit operation details
    6315             :  * @param known_coin_id row of the coin in the known_coins table
    6316             :  * @param h_payto hash of the merchant's bank account details
    6317             :  * @param extension_blocked true if an extension is blocking the wire transfer
    6318             :  * @param[in,out] exchange_timestamp time to use for the deposit (possibly updated)
    6319             :  * @param[out] balance_ok set to true if the balance was sufficient
    6320             :  * @param[out] in_conflict set to true if the deposit conflicted
    6321             :  * @return query execution status
    6322             :  */
    6323             : static enum GNUNET_DB_QueryStatus
    6324           0 : postgres_do_deposit (
    6325             :   void *cls,
    6326             :   const struct TALER_EXCHANGEDB_Deposit *deposit,
    6327             :   uint64_t known_coin_id,
    6328             :   const struct TALER_PaytoHashP *h_payto,
    6329             :   bool extension_blocked,
    6330             :   struct GNUNET_TIME_Timestamp *exchange_timestamp,
    6331             :   bool *balance_ok,
    6332             :   bool *in_conflict)
    6333             : {
    6334           0 :   struct PostgresClosure *pg = cls;
    6335           0 :   uint64_t deposit_shard = compute_shard (&deposit->merchant_pub);
    6336           0 :   struct GNUNET_PQ_QueryParam params[] = {
    6337           0 :     TALER_PQ_query_param_amount (&deposit->amount_with_fee),
    6338           0 :     GNUNET_PQ_query_param_auto_from_type (&deposit->h_contract_terms),
    6339           0 :     GNUNET_PQ_query_param_auto_from_type (&deposit->wire_salt),
    6340           0 :     GNUNET_PQ_query_param_timestamp (&deposit->timestamp),
    6341           0 :     GNUNET_PQ_query_param_timestamp (exchange_timestamp),
    6342           0 :     GNUNET_PQ_query_param_timestamp (&deposit->refund_deadline),
    6343           0 :     GNUNET_PQ_query_param_timestamp (&deposit->wire_deadline),
    6344           0 :     GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
    6345           0 :     GNUNET_PQ_query_param_string (deposit->receiver_wire_account),
    6346           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
    6347           0 :     GNUNET_PQ_query_param_uint64 (&known_coin_id),
    6348           0 :     GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
    6349           0 :     GNUNET_PQ_query_param_auto_from_type (&deposit->csig),
    6350           0 :     GNUNET_PQ_query_param_uint64 (&deposit_shard),
    6351           0 :     GNUNET_PQ_query_param_bool (extension_blocked),
    6352           0 :     (NULL == deposit->extension_details)
    6353           0 :     ? GNUNET_PQ_query_param_null ()
    6354           0 :     : TALER_PQ_query_param_json (deposit->extension_details),
    6355             :     GNUNET_PQ_query_param_end
    6356             :   };
    6357           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6358           0 :     GNUNET_PQ_result_spec_bool ("balance_ok",
    6359             :                                 balance_ok),
    6360           0 :     GNUNET_PQ_result_spec_bool ("conflicted",
    6361             :                                 in_conflict),
    6362           0 :     GNUNET_PQ_result_spec_timestamp ("exchange_timestamp",
    6363             :                                      exchange_timestamp),
    6364             :     GNUNET_PQ_result_spec_end
    6365             :   };
    6366             : 
    6367           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6368             :                                                    "call_deposit",
    6369             :                                                    params,
    6370             :                                                    rs);
    6371             : }
    6372             : 
    6373             : 
    6374             : /**
    6375             :  * Perform melt operation, checking for sufficient balance
    6376             :  * of the coin and possibly persisting the melt details.
    6377             :  *
    6378             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    6379             :  * @param rms client-contributed input for CS denominations that must be checked for idempotency, or NULL for non-CS withdrawals
    6380             :  * @param[in,out] refresh refresh operation details; the noreveal_index
    6381             :  *                is set in case the coin was already melted before
    6382             :  * @param known_coin_id row of the coin in the known_coins table
    6383             :  * @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
    6384             :  * @param[out] balance_ok set to true if the balance was sufficient
    6385             :  * @return query execution status
    6386             :  */
    6387             : static enum GNUNET_DB_QueryStatus
    6388           0 : postgres_do_melt (
    6389             :   void *cls,
    6390             :   const struct TALER_RefreshMasterSecretP *rms,
    6391             :   struct TALER_EXCHANGEDB_Refresh *refresh,
    6392             :   uint64_t known_coin_id,
    6393             :   bool *zombie_required,
    6394             :   bool *balance_ok)
    6395             : {
    6396           0 :   struct PostgresClosure *pg = cls;
    6397           0 :   struct GNUNET_PQ_QueryParam params[] = {
    6398             :     NULL == rms
    6399           0 :     ? GNUNET_PQ_query_param_null ()
    6400           0 :     : GNUNET_PQ_query_param_auto_from_type (rms),
    6401           0 :     TALER_PQ_query_param_amount (&refresh->amount_with_fee),
    6402           0 :     GNUNET_PQ_query_param_auto_from_type (&refresh->rc),
    6403           0 :     GNUNET_PQ_query_param_auto_from_type (&refresh->coin.coin_pub),
    6404           0 :     GNUNET_PQ_query_param_auto_from_type (&refresh->coin_sig),
    6405           0 :     GNUNET_PQ_query_param_uint64 (&known_coin_id),
    6406           0 :     GNUNET_PQ_query_param_uint32 (&refresh->noreveal_index),
    6407           0 :     GNUNET_PQ_query_param_bool (*zombie_required),
    6408             :     GNUNET_PQ_query_param_end
    6409             :   };
    6410             :   bool is_null;
    6411           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6412           0 :     GNUNET_PQ_result_spec_bool ("balance_ok",
    6413             :                                 balance_ok),
    6414           0 :     GNUNET_PQ_result_spec_bool ("zombie_required",
    6415             :                                 zombie_required),
    6416           0 :     GNUNET_PQ_result_spec_allow_null (
    6417             :       GNUNET_PQ_result_spec_uint32 ("noreveal_index",
    6418             :                                     &refresh->noreveal_index),
    6419             :       &is_null),
    6420             :     GNUNET_PQ_result_spec_end
    6421             :   };
    6422             :   enum GNUNET_DB_QueryStatus qs;
    6423             : 
    6424           0 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6425             :                                                  "call_melt",
    6426             :                                                  params,
    6427             :                                                  rs);
    6428           0 :   if (is_null)
    6429           0 :     refresh->noreveal_index = UINT32_MAX; /* set to very invalid value */
    6430           0 :   return qs;
    6431             : }
    6432             : 
    6433             : 
    6434             : /**
    6435             :  * Perform refund operation, checking for sufficient deposits
    6436             :  * of the coin and possibly persisting the refund details.
    6437             :  *
    6438             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    6439             :  * @param refund refund operation details
    6440             :  * @param deposit_fee deposit fee applicable for the coin, possibly refunded
    6441             :  * @param known_coin_id row of the coin in the known_coins table
    6442             :  * @param[out] not_found set if the deposit was not found
    6443             :  * @param[out] refund_ok  set if the refund succeeded (below deposit amount)
    6444             :  * @param[out] gone if the merchant was already paid
    6445             :  * @param[out] conflict set if the refund ID was re-used
    6446             :  * @return query execution status
    6447             :  */
    6448             : static enum GNUNET_DB_QueryStatus
    6449           0 : postgres_do_refund (
    6450             :   void *cls,
    6451             :   const struct TALER_EXCHANGEDB_Refund *refund,
    6452             :   const struct TALER_Amount *deposit_fee,
    6453             :   uint64_t known_coin_id,
    6454             :   bool *not_found,
    6455             :   bool *refund_ok,
    6456             :   bool *gone,
    6457             :   bool *conflict)
    6458             : {
    6459           0 :   struct PostgresClosure *pg = cls;
    6460           0 :   uint64_t deposit_shard = compute_shard (&refund->details.merchant_pub);
    6461             :   struct TALER_Amount amount_without_fee;
    6462           0 :   struct GNUNET_PQ_QueryParam params[] = {
    6463           0 :     TALER_PQ_query_param_amount (&refund->details.refund_amount),
    6464           0 :     TALER_PQ_query_param_amount (&amount_without_fee),
    6465           0 :     TALER_PQ_query_param_amount (deposit_fee),
    6466           0 :     GNUNET_PQ_query_param_auto_from_type (&refund->details.h_contract_terms),
    6467           0 :     GNUNET_PQ_query_param_uint64 (&refund->details.rtransaction_id),
    6468           0 :     GNUNET_PQ_query_param_uint64 (&deposit_shard),
    6469           0 :     GNUNET_PQ_query_param_uint64 (&known_coin_id),
    6470           0 :     GNUNET_PQ_query_param_auto_from_type (&refund->coin.coin_pub),
    6471           0 :     GNUNET_PQ_query_param_auto_from_type (&refund->details.merchant_pub),
    6472           0 :     GNUNET_PQ_query_param_auto_from_type (&refund->details.merchant_sig),
    6473             :     GNUNET_PQ_query_param_end
    6474             :   };
    6475           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6476           0 :     GNUNET_PQ_result_spec_bool ("not_found",
    6477             :                                 not_found),
    6478           0 :     GNUNET_PQ_result_spec_bool ("refund_ok",
    6479             :                                 refund_ok),
    6480           0 :     GNUNET_PQ_result_spec_bool ("gone",
    6481             :                                 gone),
    6482           0 :     GNUNET_PQ_result_spec_bool ("conflict",
    6483             :                                 conflict),
    6484             :     GNUNET_PQ_result_spec_end
    6485             :   };
    6486             : 
    6487           0 :   if (0 >
    6488           0 :       TALER_amount_subtract (&amount_without_fee,
    6489             :                              &refund->details.refund_amount,
    6490             :                              &refund->details.refund_fee))
    6491             :   {
    6492           0 :     GNUNET_break (0);
    6493           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    6494             :   }
    6495           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6496             :                                                    "call_refund",
    6497             :                                                    params,
    6498             :                                                    rs);
    6499             : }
    6500             : 
    6501             : 
    6502             : /**
    6503             :  * Perform recoup operation, checking for sufficient deposits
    6504             :  * of the coin and possibly persisting the recoup details.
    6505             :  *
    6506             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    6507             :  * @param reserve_pub public key of the reserve to credit
    6508             :  * @param reserve_out_serial_id row in the reserves_out table justifying the recoup
    6509             :  * @param coin_bks coin blinding key secret to persist
    6510             :  * @param coin_pub public key of the coin being recouped
    6511             :  * @param known_coin_id row of the @a coin_pub in the known_coins table
    6512             :  * @param coin_sig signature of the coin requesting the recoup
    6513             :  * @param[in,out] recoup_timestamp recoup timestamp, set if recoup existed
    6514             :  * @param[out] recoup_ok  set if the recoup succeeded (balance ok)
    6515             :  * @param[out] internal_failure set on internal failures
    6516             :  * @return query execution status
    6517             :  */
    6518             : static enum GNUNET_DB_QueryStatus
    6519           0 : postgres_do_recoup (
    6520             :   void *cls,
    6521             :   const struct TALER_ReservePublicKeyP *reserve_pub,
    6522             :   uint64_t reserve_out_serial_id,
    6523             :   const union TALER_DenominationBlindingKeyP *coin_bks,
    6524             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    6525             :   uint64_t known_coin_id,
    6526             :   const struct TALER_CoinSpendSignatureP *coin_sig,
    6527             :   struct GNUNET_TIME_Timestamp *recoup_timestamp,
    6528             :   bool *recoup_ok,
    6529             :   bool *internal_failure)
    6530             : {
    6531           0 :   struct PostgresClosure *pg = cls;
    6532             :   struct GNUNET_TIME_Timestamp reserve_gc
    6533           0 :     = GNUNET_TIME_relative_to_timestamp (pg->legal_reserve_expiration_time);
    6534             :   struct GNUNET_TIME_Timestamp reserve_expiration
    6535           0 :     = GNUNET_TIME_relative_to_timestamp (pg->idle_reserve_expiration_time);
    6536           0 :   struct GNUNET_PQ_QueryParam params[] = {
    6537           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    6538           0 :     GNUNET_PQ_query_param_uint64 (&reserve_out_serial_id),
    6539           0 :     GNUNET_PQ_query_param_auto_from_type (coin_bks),
    6540           0 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    6541           0 :     GNUNET_PQ_query_param_uint64 (&known_coin_id),
    6542           0 :     GNUNET_PQ_query_param_auto_from_type (coin_sig),
    6543           0 :     GNUNET_PQ_query_param_timestamp (&reserve_gc),
    6544           0 :     GNUNET_PQ_query_param_timestamp (&reserve_expiration),
    6545           0 :     GNUNET_PQ_query_param_timestamp (recoup_timestamp),
    6546             :     GNUNET_PQ_query_param_end
    6547             :   };
    6548             :   bool is_null;
    6549           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6550           0 :     GNUNET_PQ_result_spec_allow_null (
    6551             :       GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
    6552             :                                        recoup_timestamp),
    6553             :       &is_null),
    6554           0 :     GNUNET_PQ_result_spec_bool ("recoup_ok",
    6555             :                                 recoup_ok),
    6556           0 :     GNUNET_PQ_result_spec_bool ("internal_failure",
    6557             :                                 internal_failure),
    6558             :     GNUNET_PQ_result_spec_end
    6559             :   };
    6560             : 
    6561           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6562             :                                                    "call_recoup",
    6563             :                                                    params,
    6564             :                                                    rs);
    6565             : }
    6566             : 
    6567             : 
    6568             : /**
    6569             :  * Perform recoup-refresh operation, checking for sufficient deposits of the
    6570             :  * coin and possibly persisting the recoup-refresh details.
    6571             :  *
    6572             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    6573             :  * @param old_coin_pub public key of the old coin to credit
    6574             :  * @param rrc_serial row in the refresh_revealed_coins table justifying the recoup-refresh
    6575             :  * @param coin_bks coin blinding key secret to persist
    6576             :  * @param coin_pub public key of the coin being recouped
    6577             :  * @param known_coin_id row of the @a coin_pub in the known_coins table
    6578             :  * @param coin_sig signature of the coin requesting the recoup
    6579             :  * @param[in,out] recoup_timestamp recoup timestamp, set if recoup existed
    6580             :  * @param[out] recoup_ok  set if the recoup-refresh succeeded (balance ok)
    6581             :  * @param[out] internal_failure set on internal failures
    6582             :  * @return query execution status
    6583             :  */
    6584             : static enum GNUNET_DB_QueryStatus
    6585           0 : postgres_do_recoup_refresh (
    6586             :   void *cls,
    6587             :   const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
    6588             :   uint64_t rrc_serial,
    6589             :   const union TALER_DenominationBlindingKeyP *coin_bks,
    6590             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    6591             :   uint64_t known_coin_id,
    6592             :   const struct TALER_CoinSpendSignatureP *coin_sig,
    6593             :   struct GNUNET_TIME_Timestamp *recoup_timestamp,
    6594             :   bool *recoup_ok,
    6595             :   bool *internal_failure)
    6596             : {
    6597           0 :   struct PostgresClosure *pg = cls;
    6598           0 :   struct GNUNET_PQ_QueryParam params[] = {
    6599           0 :     GNUNET_PQ_query_param_auto_from_type (old_coin_pub),
    6600           0 :     GNUNET_PQ_query_param_uint64 (&rrc_serial),
    6601           0 :     GNUNET_PQ_query_param_auto_from_type (coin_bks),
    6602           0 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    6603           0 :     GNUNET_PQ_query_param_uint64 (&known_coin_id),
    6604           0 :     GNUNET_PQ_query_param_auto_from_type (coin_sig),
    6605           0 :     GNUNET_PQ_query_param_timestamp (recoup_timestamp),
    6606             :     GNUNET_PQ_query_param_end
    6607             :   };
    6608             :   bool is_null;
    6609           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6610           0 :     GNUNET_PQ_result_spec_allow_null (
    6611             :       GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
    6612             :                                        recoup_timestamp),
    6613             :       &is_null),
    6614           0 :     GNUNET_PQ_result_spec_bool ("recoup_ok",
    6615             :                                 recoup_ok),
    6616           0 :     GNUNET_PQ_result_spec_bool ("internal_failure",
    6617             :                                 internal_failure),
    6618             :     GNUNET_PQ_result_spec_end
    6619             :   };
    6620             : 
    6621           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6622             :                                                    "call_recoup_refresh",
    6623             :                                                    params,
    6624             :                                                    rs);
    6625             : }
    6626             : 
    6627             : 
    6628             : /**
    6629             :  * Closure for callbacks invoked via #postgres_get_reserve_history.
    6630             :  */
    6631             : struct ReserveHistoryContext
    6632             : {
    6633             : 
    6634             :   /**
    6635             :    * Which reserve are we building the history for?
    6636             :    */
    6637             :   const struct TALER_ReservePublicKeyP *reserve_pub;
    6638             : 
    6639             :   /**
    6640             :    * Where we build the history.
    6641             :    */
    6642             :   struct TALER_EXCHANGEDB_ReserveHistory *rh;
    6643             : 
    6644             :   /**
    6645             :    * Tail of @e rh list.
    6646             :    */
    6647             :   struct TALER_EXCHANGEDB_ReserveHistory *rh_tail;
    6648             : 
    6649             :   /**
    6650             :    * Plugin context.
    6651             :    */
    6652             :   struct PostgresClosure *pg;
    6653             : 
    6654             :   /**
    6655             :    * Sum of all credit transactions.
    6656             :    */
    6657             :   struct TALER_Amount balance_in;
    6658             : 
    6659             :   /**
    6660             :    * Sum of all debit transactions.
    6661             :    */
    6662             :   struct TALER_Amount balance_out;
    6663             : 
    6664             :   /**
    6665             :    * Set to #GNUNET_SYSERR on serious internal errors during
    6666             :    * the callbacks.
    6667             :    */
    6668             :   enum GNUNET_GenericReturnValue status;
    6669             : };
    6670             : 
    6671             : 
    6672             : /**
    6673             :  * Append and return a fresh element to the reserve
    6674             :  * history kept in @a rhc.
    6675             :  *
    6676             :  * @param rhc where the history is kept
    6677             :  * @return the fresh element that was added
    6678             :  */
    6679             : static struct TALER_EXCHANGEDB_ReserveHistory *
    6680           0 : append_rh (struct ReserveHistoryContext *rhc)
    6681             : {
    6682             :   struct TALER_EXCHANGEDB_ReserveHistory *tail;
    6683             : 
    6684           0 :   tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory);
    6685           0 :   if (NULL != rhc->rh_tail)
    6686             :   {
    6687           0 :     rhc->rh_tail->next = tail;
    6688           0 :     rhc->rh_tail = tail;
    6689             :   }
    6690             :   else
    6691             :   {
    6692           0 :     rhc->rh_tail = tail;
    6693           0 :     rhc->rh = tail;
    6694             :   }
    6695           0 :   return tail;
    6696             : }
    6697             : 
    6698             : 
    6699             : /**
    6700             :  * Add bank transfers to result set for #postgres_get_reserve_history.
    6701             :  *
    6702             :  * @param cls a `struct ReserveHistoryContext *`
    6703             :  * @param result SQL result
    6704             :  * @param num_results number of rows in @a result
    6705             :  */
    6706             : static void
    6707           0 : add_bank_to_exchange (void *cls,
    6708             :                       PGresult *result,
    6709             :                       unsigned int num_results)
    6710             : {
    6711           0 :   struct ReserveHistoryContext *rhc = cls;
    6712           0 :   struct PostgresClosure *pg = rhc->pg;
    6713             : 
    6714           0 :   while (0 < num_results)
    6715             :   {
    6716             :     struct TALER_EXCHANGEDB_BankTransfer *bt;
    6717             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    6718             : 
    6719           0 :     bt = GNUNET_new (struct TALER_EXCHANGEDB_BankTransfer);
    6720             :     {
    6721           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    6722           0 :         GNUNET_PQ_result_spec_uint64 ("wire_reference",
    6723             :                                       &bt->wire_reference),
    6724           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("credit",
    6725             :                                      &bt->amount),
    6726           0 :         GNUNET_PQ_result_spec_timestamp ("execution_date",
    6727             :                                          &bt->execution_date),
    6728           0 :         GNUNET_PQ_result_spec_string ("sender_account_details",
    6729             :                                       &bt->sender_account_details),
    6730             :         GNUNET_PQ_result_spec_end
    6731             :       };
    6732             : 
    6733           0 :       if (GNUNET_OK !=
    6734           0 :           GNUNET_PQ_extract_result (result,
    6735             :                                     rs,
    6736             :                                     --num_results))
    6737             :       {
    6738           0 :         GNUNET_break (0);
    6739           0 :         GNUNET_free (bt);
    6740           0 :         rhc->status = GNUNET_SYSERR;
    6741           0 :         return;
    6742             :       }
    6743             :     }
    6744           0 :     GNUNET_assert (0 <=
    6745             :                    TALER_amount_add (&rhc->balance_in,
    6746             :                                      &rhc->balance_in,
    6747             :                                      &bt->amount));
    6748           0 :     bt->reserve_pub = *rhc->reserve_pub;
    6749           0 :     tail = append_rh (rhc);
    6750           0 :     tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE;
    6751           0 :     tail->details.bank = bt;
    6752             :   } /* end of 'while (0 < rows)' */
    6753             : }
    6754             : 
    6755             : 
    6756             : /**
    6757             :  * Add coin withdrawals to result set for #postgres_get_reserve_history.
    6758             :  *
    6759             :  * @param cls a `struct ReserveHistoryContext *`
    6760             :  * @param result SQL result
    6761             :  * @param num_results number of rows in @a result
    6762             :  */
    6763             : static void
    6764           0 : add_withdraw_coin (void *cls,
    6765             :                    PGresult *result,
    6766             :                    unsigned int num_results)
    6767             : {
    6768           0 :   struct ReserveHistoryContext *rhc = cls;
    6769           0 :   struct PostgresClosure *pg = rhc->pg;
    6770             : 
    6771           0 :   while (0 < num_results)
    6772             :   {
    6773             :     struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc;
    6774             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    6775             : 
    6776           0 :     cbc = GNUNET_new (struct TALER_EXCHANGEDB_CollectableBlindcoin);
    6777             :     {
    6778           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    6779           0 :         GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
    6780             :                                               &cbc->h_coin_envelope),
    6781           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    6782             :                                               &cbc->denom_pub_hash),
    6783           0 :         TALER_PQ_result_spec_blinded_denom_sig ("denom_sig",
    6784             :                                                 &cbc->sig),
    6785           0 :         GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
    6786             :                                               &cbc->reserve_sig),
    6787           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    6788             :                                      &cbc->amount_with_fee),
    6789           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
    6790             :                                      &cbc->withdraw_fee),
    6791             :         GNUNET_PQ_result_spec_end
    6792             :       };
    6793             : 
    6794           0 :       if (GNUNET_OK !=
    6795           0 :           GNUNET_PQ_extract_result (result,
    6796             :                                     rs,
    6797             :                                     --num_results))
    6798             :       {
    6799           0 :         GNUNET_break (0);
    6800           0 :         GNUNET_free (cbc);
    6801           0 :         rhc->status = GNUNET_SYSERR;
    6802           0 :         return;
    6803             :       }
    6804             :     }
    6805           0 :     GNUNET_assert (0 <=
    6806             :                    TALER_amount_add (&rhc->balance_out,
    6807             :                                      &rhc->balance_out,
    6808             :                                      &cbc->amount_with_fee));
    6809           0 :     cbc->reserve_pub = *rhc->reserve_pub;
    6810           0 :     tail = append_rh (rhc);
    6811           0 :     tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN;
    6812           0 :     tail->details.withdraw = cbc;
    6813             :   }
    6814             : }
    6815             : 
    6816             : 
    6817             : /**
    6818             :  * Add recoups to result set for #postgres_get_reserve_history.
    6819             :  *
    6820             :  * @param cls a `struct ReserveHistoryContext *`
    6821             :  * @param result SQL result
    6822             :  * @param num_results number of rows in @a result
    6823             :  */
    6824             : static void
    6825           0 : add_recoup (void *cls,
    6826             :             PGresult *result,
    6827             :             unsigned int num_results)
    6828             : {
    6829           0 :   struct ReserveHistoryContext *rhc = cls;
    6830           0 :   struct PostgresClosure *pg = rhc->pg;
    6831             : 
    6832           0 :   while (0 < num_results)
    6833             :   {
    6834             :     struct TALER_EXCHANGEDB_Recoup *recoup;
    6835             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    6836             : 
    6837           0 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_Recoup);
    6838             :     {
    6839           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    6840           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    6841             :                                      &recoup->value),
    6842           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    6843             :                                               &recoup->coin.coin_pub),
    6844           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
    6845             :                                               &recoup->coin_blind),
    6846           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    6847             :                                               &recoup->coin_sig),
    6848           0 :         GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
    6849             :                                          &recoup->timestamp),
    6850           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    6851             :                                               &recoup->coin.denom_pub_hash),
    6852           0 :         TALER_PQ_result_spec_denom_sig (
    6853             :           "denom_sig",
    6854             :           &recoup->coin.denom_sig),
    6855             :         GNUNET_PQ_result_spec_end
    6856             :       };
    6857             : 
    6858           0 :       if (GNUNET_OK !=
    6859           0 :           GNUNET_PQ_extract_result (result,
    6860             :                                     rs,
    6861             :                                     --num_results))
    6862             :       {
    6863           0 :         GNUNET_break (0);
    6864           0 :         GNUNET_free (recoup);
    6865           0 :         rhc->status = GNUNET_SYSERR;
    6866           0 :         return;
    6867             :       }
    6868             :     }
    6869           0 :     GNUNET_assert (0 <=
    6870             :                    TALER_amount_add (&rhc->balance_in,
    6871             :                                      &rhc->balance_in,
    6872             :                                      &recoup->value));
    6873           0 :     recoup->reserve_pub = *rhc->reserve_pub;
    6874           0 :     tail = append_rh (rhc);
    6875           0 :     tail->type = TALER_EXCHANGEDB_RO_RECOUP_COIN;
    6876           0 :     tail->details.recoup = recoup;
    6877             :   } /* end of 'while (0 < rows)' */
    6878             : }
    6879             : 
    6880             : 
    6881             : /**
    6882             :  * Add exchange-to-bank transfers to result set for
    6883             :  * #postgres_get_reserve_history.
    6884             :  *
    6885             :  * @param cls a `struct ReserveHistoryContext *`
    6886             :  * @param result SQL result
    6887             :  * @param num_results number of rows in @a result
    6888             :  */
    6889             : static void
    6890           0 : add_exchange_to_bank (void *cls,
    6891             :                       PGresult *result,
    6892             :                       unsigned int num_results)
    6893             : {
    6894           0 :   struct ReserveHistoryContext *rhc = cls;
    6895           0 :   struct PostgresClosure *pg = rhc->pg;
    6896             : 
    6897           0 :   while (0 < num_results)
    6898             :   {
    6899             :     struct TALER_EXCHANGEDB_ClosingTransfer *closing;
    6900             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    6901             : 
    6902           0 :     closing = GNUNET_new (struct TALER_EXCHANGEDB_ClosingTransfer);
    6903             :     {
    6904           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    6905           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    6906             :                                      &closing->amount),
    6907           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
    6908             :                                      &closing->closing_fee),
    6909           0 :         GNUNET_PQ_result_spec_timestamp ("execution_date",
    6910             :                                          &closing->execution_date),
    6911           0 :         GNUNET_PQ_result_spec_string ("receiver_account",
    6912             :                                       &closing->receiver_account_details),
    6913           0 :         GNUNET_PQ_result_spec_auto_from_type ("wtid",
    6914             :                                               &closing->wtid),
    6915             :         GNUNET_PQ_result_spec_end
    6916             :       };
    6917             : 
    6918           0 :       if (GNUNET_OK !=
    6919           0 :           GNUNET_PQ_extract_result (result,
    6920             :                                     rs,
    6921             :                                     --num_results))
    6922             :       {
    6923           0 :         GNUNET_break (0);
    6924           0 :         GNUNET_free (closing);
    6925           0 :         rhc->status = GNUNET_SYSERR;
    6926           0 :         return;
    6927             :       }
    6928             :     }
    6929           0 :     GNUNET_assert (0 <=
    6930             :                    TALER_amount_add (&rhc->balance_out,
    6931             :                                      &rhc->balance_out,
    6932             :                                      &closing->amount));
    6933           0 :     closing->reserve_pub = *rhc->reserve_pub;
    6934           0 :     tail = append_rh (rhc);
    6935           0 :     tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK;
    6936           0 :     tail->details.closing = closing;
    6937             :   } /* end of 'while (0 < rows)' */
    6938             : }
    6939             : 
    6940             : 
    6941             : /**
    6942             :  * Add purse merge transfers to result set for
    6943             :  * #postgres_get_reserve_history.
    6944             :  *
    6945             :  * @param cls a `struct ReserveHistoryContext *`
    6946             :  * @param result SQL result
    6947             :  * @param num_results number of rows in @a result
    6948             :  */
    6949             : static void
    6950           0 : add_p2p_merge (void *cls,
    6951             :                PGresult *result,
    6952             :                unsigned int num_results)
    6953             : {
    6954           0 :   struct ReserveHistoryContext *rhc = cls;
    6955           0 :   struct PostgresClosure *pg = rhc->pg;
    6956             : 
    6957           0 :   while (0 < num_results)
    6958             :   {
    6959             :     struct TALER_EXCHANGEDB_PurseMerge *merge;
    6960             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    6961             : 
    6962           0 :     merge = GNUNET_new (struct TALER_EXCHANGEDB_PurseMerge);
    6963             :     {
    6964             :       uint32_t flags32;
    6965             :       struct TALER_Amount balance;
    6966           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    6967           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee",
    6968             :                                      &merge->purse_fee),
    6969           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
    6970             :                                      &balance),
    6971           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    6972             :                                      &merge->amount_with_fee),
    6973           0 :         GNUNET_PQ_result_spec_timestamp ("merge_timestamp",
    6974             :                                          &merge->merge_timestamp),
    6975           0 :         GNUNET_PQ_result_spec_timestamp ("purse_expiration",
    6976             :                                          &merge->purse_expiration),
    6977           0 :         GNUNET_PQ_result_spec_uint32 ("age_limit",
    6978             :                                       &merge->min_age),
    6979           0 :         GNUNET_PQ_result_spec_uint32 ("flags",
    6980             :                                       &flags32),
    6981           0 :         GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    6982             :                                               &merge->h_contract_terms),
    6983           0 :         GNUNET_PQ_result_spec_auto_from_type ("merge_pub",
    6984             :                                               &merge->merge_pub),
    6985           0 :         GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
    6986             :                                               &merge->purse_pub),
    6987           0 :         GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
    6988             :                                               &merge->reserve_sig),
    6989             :         GNUNET_PQ_result_spec_end
    6990             :       };
    6991             : 
    6992           0 :       if (GNUNET_OK !=
    6993           0 :           GNUNET_PQ_extract_result (result,
    6994             :                                     rs,
    6995             :                                     --num_results))
    6996             :       {
    6997           0 :         GNUNET_break (0);
    6998           0 :         GNUNET_free (merge);
    6999           0 :         rhc->status = GNUNET_SYSERR;
    7000           0 :         return;
    7001             :       }
    7002           0 :       merge->flags = (enum TALER_WalletAccountMergeFlags) flags32;
    7003           0 :       if ( (! GNUNET_TIME_absolute_is_future (
    7004           0 :               merge->merge_timestamp.abs_time)) &&
    7005           0 :            (-1 != TALER_amount_cmp (&balance,
    7006           0 :                                     &merge->amount_with_fee)) )
    7007           0 :         merge->merged = true;
    7008             :     }
    7009           0 :     if (merge->merged)
    7010           0 :       GNUNET_assert (0 <=
    7011             :                      TALER_amount_add (&rhc->balance_in,
    7012             :                                        &rhc->balance_in,
    7013             :                                        &merge->amount_with_fee));
    7014           0 :     GNUNET_assert (0 <=
    7015             :                    TALER_amount_add (&rhc->balance_out,
    7016             :                                      &rhc->balance_out,
    7017             :                                      &merge->purse_fee));
    7018           0 :     merge->reserve_pub = *rhc->reserve_pub;
    7019           0 :     tail = append_rh (rhc);
    7020           0 :     tail->type = TALER_EXCHANGEDB_RO_PURSE_MERGE;
    7021           0 :     tail->details.merge = merge;
    7022             :   }
    7023             : }
    7024             : 
    7025             : 
    7026             : /**
    7027             :  * Add paid for history requests to result set for
    7028             :  * #postgres_get_reserve_history.
    7029             :  *
    7030             :  * @param cls a `struct ReserveHistoryContext *`
    7031             :  * @param result SQL result
    7032             :  * @param num_results number of rows in @a result
    7033             :  */
    7034             : static void
    7035           0 : add_history_requests (void *cls,
    7036             :                       PGresult *result,
    7037             :                       unsigned int num_results)
    7038             : {
    7039           0 :   struct ReserveHistoryContext *rhc = cls;
    7040           0 :   struct PostgresClosure *pg = rhc->pg;
    7041             : 
    7042           0 :   while (0 < num_results)
    7043             :   {
    7044             :     struct TALER_EXCHANGEDB_HistoryRequest *history;
    7045             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    7046             : 
    7047           0 :     history = GNUNET_new (struct TALER_EXCHANGEDB_HistoryRequest);
    7048             :     {
    7049           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    7050           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
    7051             :                                      &history->history_fee),
    7052           0 :         GNUNET_PQ_result_spec_timestamp ("request_timestamp",
    7053             :                                          &history->request_timestamp),
    7054           0 :         GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
    7055             :                                               &history->reserve_sig),
    7056             :         GNUNET_PQ_result_spec_end
    7057             :       };
    7058             : 
    7059           0 :       if (GNUNET_OK !=
    7060           0 :           GNUNET_PQ_extract_result (result,
    7061             :                                     rs,
    7062             :                                     --num_results))
    7063             :       {
    7064           0 :         GNUNET_break (0);
    7065           0 :         GNUNET_free (history);
    7066           0 :         rhc->status = GNUNET_SYSERR;
    7067           0 :         return;
    7068             :       }
    7069             :     }
    7070           0 :     GNUNET_assert (0 <=
    7071             :                    TALER_amount_add (&rhc->balance_out,
    7072             :                                      &rhc->balance_out,
    7073             :                                      &history->history_fee));
    7074           0 :     history->reserve_pub = *rhc->reserve_pub;
    7075           0 :     tail = append_rh (rhc);
    7076           0 :     tail->type = TALER_EXCHANGEDB_RO_HISTORY_REQUEST;
    7077           0 :     tail->details.history = history;
    7078             :   }
    7079             : }
    7080             : 
    7081             : 
    7082             : /**
    7083             :  * Get all of the transaction history associated with the specified
    7084             :  * reserve.
    7085             :  *
    7086             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    7087             :  * @param reserve_pub public key of the reserve
    7088             :  * @param[out] balance set to the reserve balance
    7089             :  * @param[out] rhp set to known transaction history (NULL if reserve is unknown)
    7090             :  * @return transaction status
    7091             :  */
    7092             : static enum GNUNET_DB_QueryStatus
    7093           0 : postgres_get_reserve_history (void *cls,
    7094             :                               const struct TALER_ReservePublicKeyP *reserve_pub,
    7095             :                               struct TALER_Amount *balance,
    7096             :                               struct TALER_EXCHANGEDB_ReserveHistory **rhp)
    7097             : {
    7098           0 :   struct PostgresClosure *pg = cls;
    7099             :   struct ReserveHistoryContext rhc;
    7100             :   struct
    7101             :   {
    7102             :     /**
    7103             :      * Name of the prepared statement to run.
    7104             :      */
    7105             :     const char *statement;
    7106             :     /**
    7107             :      * Function to use to process the results.
    7108             :      */
    7109             :     GNUNET_PQ_PostgresResultHandler cb;
    7110           0 :   } work[] = {
    7111             :     /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */
    7112             :     { "reserves_in_get_transactions",
    7113             :       add_bank_to_exchange },
    7114             :     /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */
    7115             :     { "get_reserves_out",
    7116             :       &add_withdraw_coin },
    7117             :     /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */
    7118             :     { "recoup_by_reserve",
    7119             :       &add_recoup },
    7120             :     /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */
    7121             :     { "close_by_reserve",
    7122             :       &add_exchange_to_bank },
    7123             :     /** #TALER_EXCHANGEDB_RO_PURSE_MERGE */
    7124             :     { "merge_by_reserve",
    7125             :       &add_p2p_merge },
    7126             :     /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */
    7127             :     { "history_by_reserve",
    7128             :       &add_history_requests },
    7129             :     /* List terminator */
    7130             :     { NULL,
    7131             :       NULL }
    7132             :   };
    7133             :   enum GNUNET_DB_QueryStatus qs;
    7134           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7135           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    7136             :     GNUNET_PQ_query_param_end
    7137             :   };
    7138             : 
    7139           0 :   rhc.reserve_pub = reserve_pub;
    7140           0 :   rhc.rh = NULL;
    7141           0 :   rhc.rh_tail = NULL;
    7142           0 :   rhc.pg = pg;
    7143           0 :   rhc.status = GNUNET_OK;
    7144           0 :   GNUNET_assert (GNUNET_OK ==
    7145             :                  TALER_amount_set_zero (pg->currency,
    7146             :                                         &rhc.balance_in));
    7147           0 :   GNUNET_assert (GNUNET_OK ==
    7148             :                  TALER_amount_set_zero (pg->currency,
    7149             :                                         &rhc.balance_out));
    7150           0 :   qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */
    7151           0 :   for (unsigned int i = 0; NULL != work[i].cb; i++)
    7152             :   {
    7153           0 :     qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    7154             :                                                work[i].statement,
    7155             :                                                params,
    7156             :                                                work[i].cb,
    7157             :                                                &rhc);
    7158           0 :     if ( (0 > qs) ||
    7159           0 :          (GNUNET_OK != rhc.status) )
    7160             :       break;
    7161             :   }
    7162           0 :   if ( (qs < 0) ||
    7163           0 :        (rhc.status != GNUNET_OK) )
    7164             :   {
    7165           0 :     common_free_reserve_history (cls,
    7166             :                                  rhc.rh);
    7167           0 :     rhc.rh = NULL;
    7168           0 :     if (qs >= 0)
    7169             :     {
    7170             :       /* status == SYSERR is a very hard error... */
    7171           0 :       qs = GNUNET_DB_STATUS_HARD_ERROR;
    7172             :     }
    7173             :   }
    7174           0 :   *rhp = rhc.rh;
    7175           0 :   GNUNET_assert (0 <=
    7176             :                  TALER_amount_subtract (balance,
    7177             :                                         &rhc.balance_in,
    7178             :                                         &rhc.balance_out));
    7179           0 :   return qs;
    7180             : }
    7181             : 
    7182             : 
    7183             : /**
    7184             :  * Get a truncated transaction history associated with the specified
    7185             :  * reserve.
    7186             :  *
    7187             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    7188             :  * @param reserve_pub public key of the reserve
    7189             :  * @param[out] balance_in set to the total of inbound
    7190             :  *             transactions in the returned history
    7191             :  * @param[out] balance_out set to the total of outbound
    7192             :  *             transactions in the returned history
    7193             :  * @param[out] rhp set to known transaction history (NULL if reserve is unknown)
    7194             :  * @return transaction status
    7195             :  */
    7196             : static enum GNUNET_DB_QueryStatus
    7197           0 : postgres_get_reserve_status (void *cls,
    7198             :                              const struct TALER_ReservePublicKeyP *reserve_pub,
    7199             :                              struct TALER_Amount *balance_in,
    7200             :                              struct TALER_Amount *balance_out,
    7201             :                              struct TALER_EXCHANGEDB_ReserveHistory **rhp)
    7202             : {
    7203           0 :   struct PostgresClosure *pg = cls;
    7204             :   struct ReserveHistoryContext rhc;
    7205             :   struct
    7206             :   {
    7207             :     /**
    7208             :      * Name of the prepared statement to run.
    7209             :      */
    7210             :     const char *statement;
    7211             :     /**
    7212             :      * Function to use to process the results.
    7213             :      */
    7214             :     GNUNET_PQ_PostgresResultHandler cb;
    7215           0 :   } work[] = {
    7216             :     /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */
    7217             :     { "reserves_in_get_transactions_truncated",
    7218             :       add_bank_to_exchange },
    7219             :     /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */
    7220             :     { "get_reserves_out_truncated",
    7221             :       &add_withdraw_coin },
    7222             :     /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */
    7223             :     { "recoup_by_reserve_truncated",
    7224             :       &add_recoup },
    7225             :     /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */
    7226             :     { "close_by_reserve_truncated",
    7227             :       &add_exchange_to_bank },
    7228             :     /** #TALER_EXCHANGEDB_RO_PURSE_MERGE */
    7229             :     { "merge_by_reserve_truncated",
    7230             :       &add_p2p_merge },
    7231             :     /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */
    7232             :     { "history_by_reserve_truncated",
    7233             :       &add_history_requests },
    7234             :     /* List terminator */
    7235             :     { NULL,
    7236             :       NULL }
    7237             :   };
    7238             :   enum GNUNET_DB_QueryStatus qs;
    7239             :   struct GNUNET_TIME_Absolute timelimit;
    7240           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7241           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    7242           0 :     GNUNET_PQ_query_param_absolute_time (&timelimit),
    7243             :     GNUNET_PQ_query_param_end
    7244             :   };
    7245             : 
    7246           0 :   timelimit = GNUNET_TIME_absolute_subtract (
    7247             :     GNUNET_TIME_absolute_get (),
    7248             :     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS,
    7249             :                                    5));
    7250           0 :   rhc.reserve_pub = reserve_pub;
    7251           0 :   rhc.rh = NULL;
    7252           0 :   rhc.rh_tail = NULL;
    7253           0 :   rhc.pg = pg;
    7254           0 :   rhc.status = GNUNET_OK;
    7255           0 :   GNUNET_assert (GNUNET_OK ==
    7256             :                  TALER_amount_set_zero (pg->currency,
    7257             :                                         &rhc.balance_in));
    7258           0 :   GNUNET_assert (GNUNET_OK ==
    7259             :                  TALER_amount_set_zero (pg->currency,
    7260             :                                         &rhc.balance_out));
    7261           0 :   qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */
    7262           0 :   for (unsigned int i = 0; NULL != work[i].cb; i++)
    7263             :   {
    7264           0 :     qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    7265             :                                                work[i].statement,
    7266             :                                                params,
    7267             :                                                work[i].cb,
    7268             :                                                &rhc);
    7269           0 :     if ( (0 > qs) ||
    7270           0 :          (GNUNET_OK != rhc.status) )
    7271             :       break;
    7272             :   }
    7273           0 :   if ( (qs < 0) ||
    7274           0 :        (rhc.status != GNUNET_OK) )
    7275             :   {
    7276           0 :     common_free_reserve_history (cls,
    7277             :                                  rhc.rh);
    7278           0 :     rhc.rh = NULL;
    7279           0 :     if (qs >= 0)
    7280             :     {
    7281             :       /* status == SYSERR is a very hard error... */
    7282           0 :       qs = GNUNET_DB_STATUS_HARD_ERROR;
    7283             :     }
    7284             :   }
    7285           0 :   *rhp = rhc.rh;
    7286           0 :   *balance_in = rhc.balance_in;
    7287           0 :   *balance_out = rhc.balance_out;
    7288           0 :   return qs;
    7289             : }
    7290             : 
    7291             : 
    7292             : /**
    7293             :  * Get the balance of the specified reserve.
    7294             :  *
    7295             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    7296             :  * @param reserve_pub public key of the reserve
    7297             :  * @param[out] balance set to the reserve balance
    7298             :  * @return transaction status
    7299             :  */
    7300             : static enum GNUNET_DB_QueryStatus
    7301           0 : postgres_get_reserve_balance (void *cls,
    7302             :                               const struct TALER_ReservePublicKeyP *reserve_pub,
    7303             :                               struct TALER_Amount *balance)
    7304             : {
    7305           0 :   struct PostgresClosure *pg = cls;
    7306           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7307           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    7308             :     GNUNET_PQ_query_param_end
    7309             :   };
    7310           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    7311           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
    7312             :                                  balance),
    7313             :     GNUNET_PQ_result_spec_end
    7314             :   };
    7315             : 
    7316           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    7317             :                                                    "get_reserve_balance",
    7318             :                                                    params,
    7319             :                                                    rs);
    7320             : }
    7321             : 
    7322             : 
    7323             : /**
    7324             :  * Check if we have the specified deposit already in the database.
    7325             :  *
    7326             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    7327             :  * @param h_contract_terms contract to check for
    7328             :  * @param h_wire wire hash to check for
    7329             :  * @param coin_pub public key of the coin to check for
    7330             :  * @param merchant merchant public key to check for
    7331             :  * @param refund_deadline expected refund deadline
    7332             :  * @param[out] deposit_fee set to the deposit fee the exchange charged
    7333             :  * @param[out] exchange_timestamp set to the time when the exchange received the deposit
    7334             :  * @return 1 if we know this operation,
    7335             :  *         0 if this exact deposit is unknown to us,
    7336             :  *         otherwise transaction error status
    7337             :  */
    7338             : static enum GNUNET_DB_QueryStatus
    7339           0 : postgres_have_deposit2 (
    7340             :   void *cls,
    7341             :   const struct TALER_PrivateContractHashP *h_contract_terms,
    7342             :   const struct TALER_MerchantWireHashP *h_wire,
    7343             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    7344             :   const struct TALER_MerchantPublicKeyP *merchant,
    7345             :   struct GNUNET_TIME_Timestamp refund_deadline,
    7346             :   struct TALER_Amount *deposit_fee,
    7347             :   struct GNUNET_TIME_Timestamp *exchange_timestamp)
    7348             : {
    7349           0 :   struct PostgresClosure *pg = cls;
    7350           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7351           0 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    7352           0 :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    7353           0 :     GNUNET_PQ_query_param_auto_from_type (merchant),
    7354             :     GNUNET_PQ_query_param_end
    7355             :   };
    7356             :   struct TALER_EXCHANGEDB_Deposit deposit2;
    7357           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    7358           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    7359             :                                  &deposit2.amount_with_fee),
    7360           0 :     GNUNET_PQ_result_spec_timestamp ("wallet_timestamp",
    7361             :                                      &deposit2.timestamp),
    7362           0 :     GNUNET_PQ_result_spec_timestamp ("exchange_timestamp",
    7363             :                                      exchange_timestamp),
    7364           0 :     GNUNET_PQ_result_spec_timestamp ("refund_deadline",
    7365             :                                      &deposit2.refund_deadline),
    7366           0 :     GNUNET_PQ_result_spec_timestamp ("wire_deadline",
    7367             :                                      &deposit2.wire_deadline),
    7368           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    7369             :                                  deposit_fee),
    7370           0 :     GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
    7371             :                                           &deposit2.wire_salt),
    7372           0 :     GNUNET_PQ_result_spec_string ("receiver_wire_account",
    7373             :                                   &deposit2.receiver_wire_account),
    7374             :     GNUNET_PQ_result_spec_end
    7375             :   };
    7376             :   enum GNUNET_DB_QueryStatus qs;
    7377             :   struct TALER_MerchantWireHashP h_wire2;
    7378             : 
    7379           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    7380             :               "Getting deposits for coin %s\n",
    7381             :               TALER_B2S (coin_pub));
    7382           0 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    7383             :                                                  "get_deposit",
    7384             :                                                  params,
    7385             :                                                  rs);
    7386           0 :   if (0 >= qs)
    7387           0 :     return qs;
    7388           0 :   TALER_merchant_wire_signature_hash (deposit2.receiver_wire_account,
    7389             :                                       &deposit2.wire_salt,
    7390             :                                       &h_wire2);
    7391           0 :   GNUNET_free (deposit2.receiver_wire_account);
    7392             :   /* Now we check that the other information in @a deposit
    7393             :      also matches, and if not report inconsistencies. */
    7394           0 :   if ( (GNUNET_TIME_timestamp_cmp (refund_deadline,
    7395             :                                    !=,
    7396           0 :                                    deposit2.refund_deadline)) ||
    7397           0 :        (0 != GNUNET_memcmp (h_wire,
    7398             :                             &h_wire2) ) )
    7399             :   {
    7400             :     /* Inconsistencies detected! Does not match! */
    7401           0 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    7402             :   }
    7403           0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    7404             : }
    7405             : 
    7406             : 
    7407             : /**
    7408             :  * Aggregate all matching deposits for @a h_payto and
    7409             :  * @a merchant_pub, returning the total amounts.
    7410             :  *
    7411             :  * @param cls the @e cls of this struct with the plugin-specific state
    7412             :  * @param h_payto destination of the wire transfer
    7413             :  * @param merchant_pub public key of the merchant
    7414             :  * @param wtid wire transfer ID to set for the aggregate
    7415             :  * @param[out] total set to the sum of the total deposits minus applicable deposit fees and refunds
    7416             :  * @return transaction status
    7417             :  */
    7418             : static enum GNUNET_DB_QueryStatus
    7419           0 : postgres_aggregate (
    7420             :   void *cls,
    7421             :   const struct TALER_PaytoHashP *h_payto,
    7422             :   const struct TALER_MerchantPublicKeyP *merchant_pub,
    7423             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    7424             :   struct TALER_Amount *total)
    7425             : {
    7426           0 :   struct PostgresClosure *pg = cls;
    7427           0 :   struct GNUNET_TIME_Absolute now = {0};
    7428           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7429           0 :     GNUNET_PQ_query_param_absolute_time (&now),
    7430           0 :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    7431           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
    7432           0 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    7433             :     GNUNET_PQ_query_param_end
    7434             :   };
    7435             :   uint64_t sum_deposit_value;
    7436             :   uint64_t sum_deposit_frac;
    7437             :   uint64_t sum_refund_value;
    7438             :   uint64_t sum_refund_frac;
    7439             :   uint64_t sum_fee_value;
    7440             :   uint64_t sum_fee_frac;
    7441           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    7442           0 :     GNUNET_PQ_result_spec_uint64 ("sum_deposit_value",
    7443             :                                   &sum_deposit_value),
    7444           0 :     GNUNET_PQ_result_spec_uint64 ("sum_deposit_fraction",
    7445             :                                   &sum_deposit_frac),
    7446           0 :     GNUNET_PQ_result_spec_uint64 ("sum_refund_value",
    7447             :                                   &sum_refund_value),
    7448           0 :     GNUNET_PQ_result_spec_uint64 ("sum_refund_fraction",
    7449             :                                   &sum_refund_frac),
    7450           0 :     GNUNET_PQ_result_spec_uint64 ("sum_fee_value",
    7451             :                                   &sum_fee_value),
    7452           0 :     GNUNET_PQ_result_spec_uint64 ("sum_fee_fraction",
    7453             :                                   &sum_fee_frac),
    7454             :     GNUNET_PQ_result_spec_end
    7455             :   };
    7456             :   enum GNUNET_DB_QueryStatus qs;
    7457             :   struct TALER_Amount sum_deposit;
    7458             :   struct TALER_Amount sum_refund;
    7459             :   struct TALER_Amount sum_fee;
    7460             :   struct TALER_Amount delta;
    7461             : 
    7462           0 :   now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (),
    7463             :                                          pg->aggregator_shift);
    7464           0 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    7465             :                                                  "aggregate",
    7466             :                                                  params,
    7467             :                                                  rs);
    7468           0 :   if (qs < 0)
    7469             :   {
    7470           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    7471           0 :     return qs;
    7472             :   }
    7473           0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    7474             :   {
    7475           0 :     GNUNET_assert (GNUNET_OK ==
    7476             :                    TALER_amount_set_zero (pg->currency,
    7477             :                                           total));
    7478           0 :     return qs;
    7479             :   }
    7480           0 :   GNUNET_assert (GNUNET_OK ==
    7481             :                  TALER_amount_set_zero (pg->currency,
    7482             :                                         &sum_deposit));
    7483           0 :   GNUNET_assert (GNUNET_OK ==
    7484             :                  TALER_amount_set_zero (pg->currency,
    7485             :                                         &sum_refund));
    7486           0 :   GNUNET_assert (GNUNET_OK ==
    7487             :                  TALER_amount_set_zero (pg->currency,
    7488             :                                         &sum_fee));
    7489           0 :   sum_deposit.value    = sum_deposit_frac / TALER_AMOUNT_FRAC_BASE
    7490           0 :                          + sum_deposit_value;
    7491           0 :   sum_deposit.fraction = sum_deposit_frac % TALER_AMOUNT_FRAC_BASE;
    7492           0 :   sum_refund.value     = sum_refund_frac  / TALER_AMOUNT_FRAC_BASE
    7493           0 :                          + sum_refund_value;
    7494           0 :   sum_refund.fraction  = sum_refund_frac  % TALER_AMOUNT_FRAC_BASE;
    7495           0 :   sum_fee.value        = sum_fee_frac     / TALER_AMOUNT_FRAC_BASE
    7496           0 :                          + sum_fee_value;
    7497           0 :   sum_fee.fraction     = sum_fee_frac     % TALER_AMOUNT_FRAC_BASE; \
    7498           0 :   GNUNET_assert (0 <=
    7499             :                  TALER_amount_subtract (&delta,
    7500             :                                         &sum_deposit,
    7501             :                                         &sum_refund));
    7502           0 :   GNUNET_assert (0 <=
    7503             :                  TALER_amount_subtract (total,
    7504             :                                         &delta,
    7505             :                                         &sum_fee));
    7506           0 :   return qs;
    7507             : }
    7508             : 
    7509             : 
    7510             : /**
    7511             :  * Create a new entry in the transient aggregation table.
    7512             :  *
    7513             :  * @param cls the @e cls of this struct with the plugin-specific state
    7514             :  * @param h_payto destination of the wire transfer
    7515             :  * @param exchange_account_section exchange account to use
    7516             :  * @param merchant_pub public key of the merchant receiving the transfer
    7517             :  * @param wtid the raw wire transfer identifier to be used
    7518             :  * @param kyc_requirement_row row in legitimization_requirements that need to be satisfied to continue, or 0 for none
    7519             :  * @param total amount to be wired in the future
    7520             :  * @return transaction status
    7521             :  */
    7522             : static enum GNUNET_DB_QueryStatus
    7523           0 : postgres_create_aggregation_transient (
    7524             :   void *cls,
    7525             :   const struct TALER_PaytoHashP *h_payto,
    7526             :   const char *exchange_account_section,
    7527             :   const struct TALER_MerchantPublicKeyP *merchant_pub,
    7528             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    7529             :   uint64_t kyc_requirement_row,
    7530             :   const struct TALER_Amount *total)
    7531             : {
    7532           0 :   struct PostgresClosure *pg = cls;
    7533           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7534           0 :     TALER_PQ_query_param_amount (total),
    7535           0 :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    7536           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
    7537           0 :     GNUNET_PQ_query_param_uint64 (&kyc_requirement_row),
    7538           0 :     GNUNET_PQ_query_param_string (exchange_account_section),
    7539           0 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    7540             :     GNUNET_PQ_query_param_end
    7541             :   };
    7542             : 
    7543           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    7544             :                                              "create_aggregation_transient",
    7545             :                                              params);
    7546             : }
    7547             : 
    7548             : 
    7549             : /**
    7550             :  * Find existing entry in the transient aggregation table.
    7551             :  *
    7552             :  * @param cls the @e cls of this struct with the plugin-specific state
    7553             :  * @param h_payto destination of the wire transfer
    7554             :  * @param merchant_pub public key of the merchant receiving the transfer
    7555             :  * @param exchange_account_section exchange account to use
    7556             :  * @param[out] wtid set to the raw wire transfer identifier to be used
    7557             :  * @param[out] total existing amount to be wired in the future
    7558             :  * @return transaction status
    7559             :  */
    7560             : static enum GNUNET_DB_QueryStatus
    7561           0 : postgres_select_aggregation_transient (
    7562             :   void *cls,
    7563             :   const struct TALER_PaytoHashP *h_payto,
    7564             :   const struct TALER_MerchantPublicKeyP *merchant_pub,
    7565             :   const char *exchange_account_section,
    7566             :   struct TALER_WireTransferIdentifierRawP *wtid,
    7567             :   struct TALER_Amount *total)
    7568             : {
    7569           0 :   struct PostgresClosure *pg = cls;
    7570           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7571           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
    7572           0 :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    7573           0 :     GNUNET_PQ_query_param_string (exchange_account_section),
    7574             :     GNUNET_PQ_query_param_end
    7575             :   };
    7576           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    7577           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    7578             :                                  total),
    7579           0 :     GNUNET_PQ_result_spec_auto_from_type ("wtid_raw",
    7580             :                                           wtid),
    7581             :     GNUNET_PQ_result_spec_end
    7582             :   };
    7583             : 
    7584           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    7585             :                                                    "select_aggregation_transient",
    7586             :                                                    params,
    7587             :                                                    rs);
    7588             : }
    7589             : 
    7590             : 
    7591             : /**
    7592             :  * Closure for #get_refunds_cb().
    7593             :  */
    7594             : struct FindAggregationTransientContext
    7595             : {
    7596             :   /**
    7597             :    * Function to call on each result.
    7598             :    */
    7599             :   TALER_EXCHANGEDB_TransientAggregationCallback cb;
    7600             : 
    7601             :   /**
    7602             :    * Closure for @a cb.
    7603             :    */
    7604             :   void *cb_cls;
    7605             : 
    7606             :   /**
    7607             :    * Plugin context.
    7608             :    */
    7609             :   struct PostgresClosure *pg;
    7610             : 
    7611             :   /**
    7612             :    * Set to #GNUNET_SYSERR on error.
    7613             :    */
    7614             :   enum GNUNET_GenericReturnValue status;
    7615             : };
    7616             : 
    7617             : 
    7618             : /**
    7619             :  * Function to be called with the results of a SELECT statement
    7620             :  * that has returned @a num_results results.
    7621             :  *
    7622             :  * @param cls closure of type `struct SelectRefundContext *`
    7623             :  * @param result the postgres result
    7624             :  * @param num_results the number of results in @a result
    7625             :  */
    7626             : static void
    7627           0 : get_transients_cb (void *cls,
    7628             :                    PGresult *result,
    7629             :                    unsigned int num_results)
    7630             : {
    7631           0 :   struct FindAggregationTransientContext *srctx = cls;
    7632           0 :   struct PostgresClosure *pg = srctx->pg;
    7633             : 
    7634           0 :   for (unsigned int i = 0; i<num_results; i++)
    7635             :   {
    7636             :     struct TALER_Amount amount;
    7637             :     char *payto_uri;
    7638             :     struct TALER_WireTransferIdentifierRawP wtid;
    7639             :     struct TALER_MerchantPublicKeyP merchant_pub;
    7640           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
    7641           0 :       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    7642             :                                             &merchant_pub),
    7643           0 :       GNUNET_PQ_result_spec_auto_from_type ("wtid_raw",
    7644             :                                             &wtid),
    7645           0 :       GNUNET_PQ_result_spec_string ("payto_uri",
    7646             :                                     &payto_uri),
    7647           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    7648             :                                    &amount),
    7649             :       GNUNET_PQ_result_spec_end
    7650             :     };
    7651             :     bool cont;
    7652             : 
    7653           0 :     if (GNUNET_OK !=
    7654           0 :         GNUNET_PQ_extract_result (result,
    7655             :                                   rs,
    7656             :                                   i))
    7657             :     {
    7658           0 :       GNUNET_break (0);
    7659           0 :       srctx->status = GNUNET_SYSERR;
    7660           0 :       return;
    7661             :     }
    7662           0 :     cont = srctx->cb (srctx->cb_cls,
    7663             :                       payto_uri,
    7664             :                       &wtid,
    7665             :                       &merchant_pub,
    7666             :                       &amount);
    7667           0 :     GNUNET_free (payto_uri);
    7668           0 :     if (! cont)
    7669           0 :       break;
    7670             :   }
    7671             : }
    7672             : 
    7673             : 
    7674             : /**
    7675             :  * Find existing entry in the transient aggregation table.
    7676             :  *
    7677             :  * @param cls the @e cls of this struct with the plugin-specific state
    7678             :  * @param h_payto destination of the wire transfer
    7679             :  * @param cb function to call on each matching entry
    7680             :  * @param cb_cls closure for @a cb
    7681             :  * @return transaction status
    7682             :  */
    7683             : static enum GNUNET_DB_QueryStatus
    7684           0 : postgres_find_aggregation_transient (
    7685             :   void *cls,
    7686             :   const struct TALER_PaytoHashP *h_payto,
    7687             :   TALER_EXCHANGEDB_TransientAggregationCallback cb,
    7688             :   void *cb_cls)
    7689             : {
    7690           0 :   struct PostgresClosure *pg = cls;
    7691             :   enum GNUNET_DB_QueryStatus qs;
    7692           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7693           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
    7694             :     GNUNET_PQ_query_param_end
    7695             :   };
    7696           0 :   struct FindAggregationTransientContext srctx = {
    7697             :     .cb = cb,
    7698             :     .cb_cls = cb_cls,
    7699             :     .pg = pg,
    7700             :     .status = GNUNET_OK
    7701             :   };
    7702             : 
    7703           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    7704             :                                              "find_transient_aggregations",
    7705             :                                              params,
    7706             :                                              &get_transients_cb,
    7707             :                                              &srctx);
    7708           0 :   if (GNUNET_SYSERR == srctx.status)
    7709           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    7710           0 :   return qs;
    7711             : }
    7712             : 
    7713             : 
    7714             : /**
    7715             :  * Update existing entry in the transient aggregation table.
    7716             :  * @a h_payto is only needed for query performance.
    7717             :  *
    7718             :  * @param cls the @e cls of this struct with the plugin-specific state
    7719             :  * @param h_payto destination of the wire transfer
    7720             :  * @param wtid the raw wire transfer identifier to update
    7721             :  * @param kyc_requirement_row row in legitimization_requirements that need to be satisfied to continue, or 0 for none
    7722             :  * @param total new total amount to be wired in the future
    7723             :  * @return transaction status
    7724             :  */
    7725             : static enum GNUNET_DB_QueryStatus
    7726           0 : postgres_update_aggregation_transient (
    7727             :   void *cls,
    7728             :   const struct TALER_PaytoHashP *h_payto,
    7729             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    7730             :   uint64_t kyc_requirement_row,
    7731             :   const struct TALER_Amount *total)
    7732             : {
    7733           0 :   struct PostgresClosure *pg = cls;
    7734           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7735           0 :     TALER_PQ_query_param_amount (total),
    7736           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
    7737           0 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    7738           0 :     GNUNET_PQ_query_param_uint64 (&kyc_requirement_row),
    7739             :     GNUNET_PQ_query_param_end
    7740             :   };
    7741             : 
    7742           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    7743             :                                              "update_aggregation_transient",
    7744             :                                              params);
    7745             : }
    7746             : 
    7747             : 
    7748             : /**
    7749             :  * Delete existing entry in the transient aggregation table.
    7750             :  * @a h_payto is only needed for query performance.
    7751             :  *
    7752             :  * @param cls the @e cls of this struct with the plugin-specific state
    7753             :  * @param h_payto destination of the wire transfer
    7754             :  * @param wtid the raw wire transfer identifier to update
    7755             :  * @return transaction status
    7756             :  */
    7757             : static enum GNUNET_DB_QueryStatus
    7758           0 : postgres_delete_aggregation_transient (
    7759             :   void *cls,
    7760             :   const struct TALER_PaytoHashP *h_payto,
    7761             :   const struct TALER_WireTransferIdentifierRawP *wtid)
    7762             : {
    7763           0 :   struct PostgresClosure *pg = cls;
    7764           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7765           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
    7766           0 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    7767             :     GNUNET_PQ_query_param_end
    7768             :   };
    7769             : 
    7770           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    7771             :                                              "delete_aggregation_transient",
    7772             :                                              params);
    7773             : }
    7774             : 
    7775             : 
    7776             : /**
    7777             :  * Obtain information about deposits that are ready to be executed.  Such
    7778             :  * deposits must not be marked as "done", the execution time must be
    7779             :  * in the past, and the KYC status must be 'ok'.
    7780             :  *
    7781             :  * @param cls the @e cls of this struct with the plugin-specific state
    7782             :  * @param start_shard_row minimum shard row to select
    7783             :  * @param end_shard_row maximum shard row to select (inclusive)
    7784             :  * @param[out] merchant_pub set to the public key of a merchant with a ready deposit
    7785             :  * @param[out] payto_uri set to the account of the merchant, to be freed by caller
    7786             :  * @return transaction status code
    7787             :  */
    7788             : static enum GNUNET_DB_QueryStatus
    7789           0 : postgres_get_ready_deposit (void *cls,
    7790             :                             uint64_t start_shard_row,
    7791             :                             uint64_t end_shard_row,
    7792             :                             struct TALER_MerchantPublicKeyP *merchant_pub,
    7793             :                             char **payto_uri)
    7794             : {
    7795           0 :   struct PostgresClosure *pg = cls;
    7796           0 :   struct GNUNET_TIME_Absolute now = {0};
    7797           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7798           0 :     GNUNET_PQ_query_param_absolute_time (&now),
    7799           0 :     GNUNET_PQ_query_param_uint64 (&start_shard_row),
    7800           0 :     GNUNET_PQ_query_param_uint64 (&end_shard_row),
    7801             :     GNUNET_PQ_query_param_end
    7802             :   };
    7803           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    7804           0 :     GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    7805             :                                           merchant_pub),
    7806           0 :     GNUNET_PQ_result_spec_string ("payto_uri",
    7807             :                                   payto_uri),
    7808             :     GNUNET_PQ_result_spec_end
    7809             :   };
    7810             : 
    7811           0 :   now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (),
    7812             :                                          pg->aggregator_shift);
    7813           0 :   GNUNET_assert (start_shard_row < end_shard_row);
    7814           0 :   GNUNET_assert (end_shard_row <= INT32_MAX);
    7815           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    7816             :               "Finding ready deposits by deadline %s (%llu)\n",
    7817             :               GNUNET_TIME_absolute2s (now),
    7818             :               (unsigned long long) now.abs_value_us);
    7819           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    7820             :                                                    "deposits_get_ready",
    7821             :                                                    params,
    7822             :                                                    rs);
    7823             : }
    7824             : 
    7825             : 
    7826             : /**
    7827             :  * Retrieve the record for a known coin.
    7828             :  *
    7829             :  * @param cls the plugin closure
    7830             :  * @param coin_pub the public key of the coin to search for
    7831             :  * @param coin_info place holder for the returned coin information object
    7832             :  * @return transaction status code
    7833             :  */
    7834             : static enum GNUNET_DB_QueryStatus
    7835           0 : postgres_get_known_coin (void *cls,
    7836             :                          const struct TALER_CoinSpendPublicKeyP *coin_pub,
    7837             :                          struct TALER_CoinPublicInfo *coin_info)
    7838             : {
    7839           0 :   struct PostgresClosure *pg = cls;
    7840           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7841           0 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    7842             :     GNUNET_PQ_query_param_end
    7843             :   };
    7844           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    7845           0 :     GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    7846             :                                           &coin_info->denom_pub_hash),
    7847           0 :     GNUNET_PQ_result_spec_allow_null (
    7848           0 :       GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
    7849             :                                             &coin_info->h_age_commitment),
    7850             :       &coin_info->no_age_commitment),
    7851           0 :     TALER_PQ_result_spec_denom_sig ("denom_sig",
    7852             :                                     &coin_info->denom_sig),
    7853             :     GNUNET_PQ_result_spec_end
    7854             :   };
    7855             : 
    7856           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    7857             :               "Getting known coin data for coin %s\n",
    7858             :               TALER_B2S (coin_pub));
    7859           0 :   coin_info->coin_pub = *coin_pub;
    7860           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    7861             :                                                    "get_known_coin",
    7862             :                                                    params,
    7863             :                                                    rs);
    7864             : }
    7865             : 
    7866             : 
    7867             : /**
    7868             :  * Retrieve the denomination of a known coin.
    7869             :  *
    7870             :  * @param cls the plugin closure
    7871             :  * @param coin_pub the public key of the coin to search for
    7872             :  * @param[out] known_coin_id set to the ID of the coin in the known_coins table
    7873             :  * @param[out] denom_hash where to store the hash of the coins denomination
    7874             :  * @return transaction status code
    7875             :  */
    7876             : static enum GNUNET_DB_QueryStatus
    7877           0 : postgres_get_coin_denomination (
    7878             :   void *cls,
    7879             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    7880             :   uint64_t *known_coin_id,
    7881             :   struct TALER_DenominationHashP *denom_hash)
    7882             : {
    7883           0 :   struct PostgresClosure *pg = cls;
    7884           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7885           0 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    7886             :     GNUNET_PQ_query_param_end
    7887             :   };
    7888           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    7889           0 :     GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    7890             :                                           denom_hash),
    7891           0 :     GNUNET_PQ_result_spec_uint64 ("known_coin_id",
    7892             :                                   known_coin_id),
    7893             :     GNUNET_PQ_result_spec_end
    7894             :   };
    7895             : 
    7896           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    7897             :               "Getting coin denomination of coin %s\n",
    7898             :               TALER_B2S (coin_pub));
    7899           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    7900             :                                                    "get_coin_denomination",
    7901             :                                                    params,
    7902             :                                                    rs);
    7903             : }
    7904             : 
    7905             : 
    7906             : /**
    7907             :  * Count the number of known coins by denomination.
    7908             :  *
    7909             :  * @param cls database connection plugin state
    7910             :  * @param denom_pub_hash denomination to count by
    7911             :  * @return number of coins if non-negative, otherwise an `enum GNUNET_DB_QueryStatus`
    7912             :  */
    7913             : static long long
    7914           0 : postgres_count_known_coins (void *cls,
    7915             :                             const struct
    7916             :                             TALER_DenominationHashP *denom_pub_hash)
    7917             : {
    7918           0 :   struct PostgresClosure *pg = cls;
    7919             :   uint64_t count;
    7920           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7921           0 :     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
    7922             :     GNUNET_PQ_query_param_end
    7923             :   };
    7924           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    7925           0 :     GNUNET_PQ_result_spec_uint64 ("count",
    7926             :                                   &count),
    7927             :     GNUNET_PQ_result_spec_end
    7928             :   };
    7929             :   enum GNUNET_DB_QueryStatus qs;
    7930             : 
    7931           0 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    7932             :                                                  "count_known_coins",
    7933             :                                                  params,
    7934             :                                                  rs);
    7935           0 :   if (0 > qs)
    7936           0 :     return (long long) qs;
    7937           0 :   return (long long) count;
    7938             : }
    7939             : 
    7940             : 
    7941             : /**
    7942             :  * Make sure the given @a coin is known to the database.
    7943             :  *
    7944             :  * @param cls database connection plugin state
    7945             :  * @param coin the coin that must be made known
    7946             :  * @param[out] known_coin_id set to the unique row of the coin
    7947             :  * @param[out] denom_hash set to the denomination hash of the existing
    7948             :  *             coin (for conflict error reporting)
    7949             :  * @param[out] h_age_commitment  set to the conflicting age commitment hash on conflict
    7950             :  * @return database transaction status, non-negative on success
    7951             :  */
    7952             : static enum TALER_EXCHANGEDB_CoinKnownStatus
    7953           0 : postgres_ensure_coin_known (void *cls,
    7954             :                             const struct TALER_CoinPublicInfo *coin,
    7955             :                             uint64_t *known_coin_id,
    7956             :                             struct TALER_DenominationHashP *denom_hash,
    7957             :                             struct TALER_AgeCommitmentHash *h_age_commitment)
    7958             : {
    7959           0 :   struct PostgresClosure *pg = cls;
    7960             :   enum GNUNET_DB_QueryStatus qs;
    7961             :   bool existed;
    7962           0 :   bool is_denom_pub_hash_null = false;
    7963           0 :   bool is_age_hash_null = false;
    7964           0 :   struct GNUNET_PQ_QueryParam params[] = {
    7965           0 :     GNUNET_PQ_query_param_auto_from_type (&coin->coin_pub),
    7966           0 :     GNUNET_PQ_query_param_auto_from_type (&coin->denom_pub_hash),
    7967           0 :     GNUNET_PQ_query_param_auto_from_type (&coin->h_age_commitment),
    7968           0 :     TALER_PQ_query_param_denom_sig (&coin->denom_sig),
    7969             :     GNUNET_PQ_query_param_end
    7970             :   };
    7971           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    7972           0 :     GNUNET_PQ_result_spec_bool ("existed",
    7973             :                                 &existed),
    7974           0 :     GNUNET_PQ_result_spec_uint64 ("known_coin_id",
    7975             :                                   known_coin_id),
    7976           0 :     GNUNET_PQ_result_spec_allow_null (
    7977             :       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    7978             :                                             denom_hash),
    7979             :       &is_denom_pub_hash_null),
    7980           0 :     GNUNET_PQ_result_spec_allow_null (
    7981             :       GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
    7982             :                                             h_age_commitment),
    7983             :       &is_age_hash_null),
    7984             :     GNUNET_PQ_result_spec_end
    7985             :   };
    7986             : 
    7987           0 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    7988             :                                                  "insert_known_coin",
    7989             :                                                  params,
    7990             :                                                  rs);
    7991           0 :   switch (qs)
    7992             :   {
    7993           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
    7994           0 :     GNUNET_break (0);
    7995           0 :     return TALER_EXCHANGEDB_CKS_HARD_FAIL;
    7996           0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
    7997           0 :     return TALER_EXCHANGEDB_CKS_SOFT_FAIL;
    7998           0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    7999           0 :     GNUNET_break (0); /* should be impossible */
    8000           0 :     return TALER_EXCHANGEDB_CKS_HARD_FAIL;
    8001           0 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    8002           0 :     if (! existed)
    8003           0 :       return TALER_EXCHANGEDB_CKS_ADDED;
    8004           0 :     break; /* continued below */
    8005             :   }
    8006             : 
    8007           0 :   if ( (! is_denom_pub_hash_null) &&
    8008           0 :        (0 != GNUNET_memcmp (&denom_hash->hash,
    8009             :                             &coin->denom_pub_hash.hash)) )
    8010             :   {
    8011           0 :     GNUNET_break_op (0);
    8012           0 :     return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT;
    8013             :   }
    8014             : 
    8015           0 :   if ( (! is_age_hash_null) &&
    8016           0 :        (0 != GNUNET_memcmp (h_age_commitment,
    8017             :                             &coin->h_age_commitment)) )
    8018             :   {
    8019           0 :     GNUNET_break (GNUNET_is_zero (h_age_commitment));
    8020           0 :     GNUNET_break_op (0);
    8021           0 :     return TALER_EXCHANGEDB_CKS_AGE_CONFLICT;
    8022             :   }
    8023             : 
    8024           0 :   return TALER_EXCHANGEDB_CKS_PRESENT;
    8025             : }
    8026             : 
    8027             : 
    8028             : /**
    8029             :  * Insert information about deposited coin into the database.
    8030             :  *
    8031             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    8032             :  * @param exchange_timestamp time the exchange received the deposit request
    8033             :  * @param deposit deposit information to store
    8034             :  * @return query result status
    8035             :  */
    8036             : static enum GNUNET_DB_QueryStatus
    8037           0 : postgres_insert_deposit (void *cls,
    8038             :                          struct GNUNET_TIME_Timestamp exchange_timestamp,
    8039             :                          const struct TALER_EXCHANGEDB_Deposit *deposit)
    8040             : {
    8041           0 :   struct PostgresClosure *pg = cls;
    8042             :   struct TALER_PaytoHashP h_payto;
    8043             :   enum GNUNET_DB_QueryStatus qs;
    8044             : 
    8045           0 :   qs = setup_wire_target (pg,
    8046           0 :                           deposit->receiver_wire_account,
    8047             :                           &h_payto);
    8048           0 :   if (qs < 0)
    8049           0 :     return qs;
    8050           0 :   if (GNUNET_TIME_timestamp_cmp (deposit->wire_deadline,
    8051             :                                  <,
    8052             :                                  deposit->refund_deadline))
    8053             :   {
    8054           0 :     GNUNET_break (0);
    8055             :   }
    8056             :   {
    8057           0 :     uint64_t shard = compute_shard (&deposit->merchant_pub);
    8058           0 :     struct GNUNET_PQ_QueryParam params[] = {
    8059           0 :       GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
    8060           0 :       TALER_PQ_query_param_amount (&deposit->amount_with_fee),
    8061           0 :       GNUNET_PQ_query_param_timestamp (&deposit->timestamp),
    8062           0 :       GNUNET_PQ_query_param_timestamp (&deposit->refund_deadline),
    8063           0 :       GNUNET_PQ_query_param_timestamp (&deposit->wire_deadline),
    8064           0 :       GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
    8065           0 :       GNUNET_PQ_query_param_auto_from_type (&deposit->h_contract_terms),
    8066           0 :       GNUNET_PQ_query_param_auto_from_type (&deposit->wire_salt),
    8067           0 :       GNUNET_PQ_query_param_auto_from_type (&h_payto),
    8068           0 :       GNUNET_PQ_query_param_auto_from_type (&deposit->csig),
    8069           0 :       GNUNET_PQ_query_param_timestamp (&exchange_timestamp),
    8070           0 :       GNUNET_PQ_query_param_uint64 (&shard),
    8071             :       GNUNET_PQ_query_param_end
    8072             :     };
    8073             : 
    8074           0 :     GNUNET_assert (shard <= INT32_MAX);
    8075           0 :     GNUNET_log (
    8076             :       GNUNET_ERROR_TYPE_INFO,
    8077             :       "Inserting deposit to be executed at %s (%llu/%llu)\n",
    8078             :       GNUNET_TIME_timestamp2s (deposit->wire_deadline),
    8079             :       (unsigned long long) deposit->wire_deadline.abs_time.abs_value_us,
    8080             :       (unsigned long long) deposit->refund_deadline.abs_time.abs_value_us);
    8081           0 :     return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    8082             :                                                "insert_deposit",
    8083             :                                                params);
    8084             :   }
    8085             : }
    8086             : 
    8087             : 
    8088             : /**
    8089             :  * Insert information about refunded coin into the database.
    8090             :  *
    8091             :  * @param cls the @e cls of this struct with the plugin-specific state
    8092             :  * @param refund refund information to store
    8093             :  * @return query result status
    8094             :  */
    8095             : static enum GNUNET_DB_QueryStatus
    8096           0 : postgres_insert_refund (void *cls,
    8097             :                         const struct TALER_EXCHANGEDB_Refund *refund)
    8098             : {
    8099           0 :   struct PostgresClosure *pg = cls;
    8100           0 :   struct GNUNET_PQ_QueryParam params[] = {
    8101           0 :     GNUNET_PQ_query_param_auto_from_type (&refund->coin.coin_pub),
    8102           0 :     GNUNET_PQ_query_param_auto_from_type (&refund->details.merchant_pub),
    8103           0 :     GNUNET_PQ_query_param_auto_from_type (&refund->details.merchant_sig),
    8104           0 :     GNUNET_PQ_query_param_auto_from_type (&refund->details.h_contract_terms),
    8105           0 :     GNUNET_PQ_query_param_uint64 (&refund->details.rtransaction_id),
    8106           0 :     TALER_PQ_query_param_amount (&refund->details.refund_amount),
    8107             :     GNUNET_PQ_query_param_end
    8108             :   };
    8109             : 
    8110           0 :   GNUNET_assert (GNUNET_YES ==
    8111             :                  TALER_amount_cmp_currency (&refund->details.refund_amount,
    8112             :                                             &refund->details.refund_fee));
    8113           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    8114             :                                              "insert_refund",
    8115             :                                              params);
    8116             : }
    8117             : 
    8118             : 
    8119             : /**
    8120             :  * Closure for #get_refunds_cb().
    8121             :  */
    8122             : struct SelectRefundContext
    8123             : {
    8124             :   /**
    8125             :    * Function to call on each result.
    8126             :    */
    8127             :   TALER_EXCHANGEDB_RefundCoinCallback cb;
    8128             : 
    8129             :   /**
    8130             :    * Closure for @a cb.
    8131             :    */
    8132             :   void *cb_cls;
    8133             : 
    8134             :   /**
    8135             :    * Plugin context.
    8136             :    */
    8137             :   struct PostgresClosure *pg;
    8138             : 
    8139             :   /**
    8140             :    * Set to #GNUNET_SYSERR on error.
    8141             :    */
    8142             :   int status;
    8143             : };
    8144             : 
    8145             : 
    8146             : /**
    8147             :  * Function to be called with the results of a SELECT statement
    8148             :  * that has returned @a num_results results.
    8149             :  *
    8150             :  * @param cls closure of type `struct SelectRefundContext *`
    8151             :  * @param result the postgres result
    8152             :  * @param num_results the number of results in @a result
    8153             :  */
    8154             : static void
    8155           0 : get_refunds_cb (void *cls,
    8156             :                 PGresult *result,
    8157             :                 unsigned int num_results)
    8158             : {
    8159           0 :   struct SelectRefundContext *srctx = cls;
    8160           0 :   struct PostgresClosure *pg = srctx->pg;
    8161             : 
    8162           0 :   for (unsigned int i = 0; i<num_results; i++)
    8163             :   {
    8164             :     struct TALER_Amount amount_with_fee;
    8165           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
    8166           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    8167             :                                    &amount_with_fee),
    8168             :       GNUNET_PQ_result_spec_end
    8169             :     };
    8170             : 
    8171           0 :     if (GNUNET_OK !=
    8172           0 :         GNUNET_PQ_extract_result (result,
    8173             :                                   rs,
    8174             :                                   i))
    8175             :     {
    8176           0 :       GNUNET_break (0);
    8177           0 :       srctx->status = GNUNET_SYSERR;
    8178           0 :       return;
    8179             :     }
    8180           0 :     if (GNUNET_OK !=
    8181           0 :         srctx->cb (srctx->cb_cls,
    8182             :                    &amount_with_fee))
    8183           0 :       return;
    8184             :   }
    8185             : }
    8186             : 
    8187             : 
    8188             : /**
    8189             :  * Select refunds by @a coin_pub, @a merchant_pub and @a h_contract.
    8190             :  *
    8191             :  * @param cls closure of plugin
    8192             :  * @param coin_pub coin to get refunds for
    8193             :  * @param merchant_pub merchant to get refunds for
    8194             :  * @param h_contract contract (hash) to get refunds for
    8195             :  * @param cb function to call for each refund found
    8196             :  * @param cb_cls closure for @a cb
    8197             :  * @return query result status
    8198             :  */
    8199             : static enum GNUNET_DB_QueryStatus
    8200           0 : postgres_select_refunds_by_coin (
    8201             :   void *cls,
    8202             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    8203             :   const struct TALER_MerchantPublicKeyP *merchant_pub,
    8204             :   const struct TALER_PrivateContractHashP *h_contract,
    8205             :   TALER_EXCHANGEDB_RefundCoinCallback cb,
    8206             :   void *cb_cls)
    8207             : {
    8208           0 :   struct PostgresClosure *pg = cls;
    8209             :   enum GNUNET_DB_QueryStatus qs;
    8210           0 :   struct GNUNET_PQ_QueryParam params[] = {
    8211           0 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    8212           0 :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    8213           0 :     GNUNET_PQ_query_param_auto_from_type (h_contract),
    8214             :     GNUNET_PQ_query_param_end
    8215             :   };
    8216           0 :   struct SelectRefundContext srctx = {
    8217             :     .cb = cb,
    8218             :     .cb_cls = cb_cls,
    8219             :     .pg = pg,
    8220             :     .status = GNUNET_OK
    8221             :   };
    8222             : 
    8223           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    8224             :                                              "get_refunds_by_coin_and_contract",
    8225             :                                              params,
    8226             :                                              &get_refunds_cb,
    8227             :                                              &srctx);
    8228           0 :   if (GNUNET_SYSERR == srctx.status)
    8229           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    8230           0 :   return qs;
    8231             : }
    8232             : 
    8233             : 
    8234             : /**
    8235             :  * Lookup refresh melt commitment data under the given @a rc.
    8236             :  *
    8237             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    8238             :  * @param rc commitment hash to use to locate the operation
    8239             :  * @param[out] melt where to store the result; note that
    8240             :  *             melt->session.coin.denom_sig will be set to NULL
    8241             :  *             and is not fetched by this routine (as it is not needed by the client)
    8242             :  * @param[out] melt_serial_id set to the row ID of @a rc in the refresh_commitments table
    8243             :  * @return transaction status
    8244             :  */
    8245             : static enum GNUNET_DB_QueryStatus
    8246           0 : postgres_get_melt (void *cls,
    8247             :                    const struct TALER_RefreshCommitmentP *rc,
    8248             :                    struct TALER_EXCHANGEDB_Melt *melt,
    8249             :                    uint64_t *melt_serial_id)
    8250             : {
    8251           0 :   struct PostgresClosure *pg = cls;
    8252             :   bool h_age_commitment_is_null;
    8253           0 :   struct GNUNET_PQ_QueryParam params[] = {
    8254           0 :     GNUNET_PQ_query_param_auto_from_type (rc),
    8255             :     GNUNET_PQ_query_param_end
    8256             :   };
    8257           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    8258           0 :     GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    8259             :                                           &melt->session.coin.
    8260             :                                           denom_pub_hash),
    8261           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
    8262             :                                  &melt->melt_fee),
    8263           0 :     GNUNET_PQ_result_spec_uint32 ("noreveal_index",
    8264             :                                   &melt->session.noreveal_index),
    8265           0 :     GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
    8266             :                                           &melt->session.coin.coin_pub),
    8267           0 :     GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
    8268             :                                           &melt->session.coin_sig),
    8269           0 :     GNUNET_PQ_result_spec_allow_null (
    8270           0 :       GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
    8271             :                                             &melt->session.coin.h_age_commitment),
    8272             :       &h_age_commitment_is_null),
    8273           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    8274             :                                  &melt->session.amount_with_fee),
    8275           0 :     GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
    8276             :                                   melt_serial_id),
    8277             :     GNUNET_PQ_result_spec_end
    8278             :   };
    8279             :   enum GNUNET_DB_QueryStatus qs;
    8280             : 
    8281           0 :   memset (&melt->session.coin.denom_sig,
    8282             :           0,
    8283             :           sizeof (melt->session.coin.denom_sig));
    8284           0 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    8285             :                                                  "get_melt",
    8286             :                                                  params,
    8287             :                                                  rs);
    8288           0 :   if (h_age_commitment_is_null)
    8289           0 :     memset (&melt->session.coin.h_age_commitment,
    8290             :             0,
    8291             :             sizeof(melt->session.coin.h_age_commitment));
    8292             : 
    8293           0 :   melt->session.rc = *rc;
    8294           0 :   return qs;
    8295             : }
    8296             : 
    8297             : 
    8298             : /**
    8299             :  * Store in the database which coin(s) the wallet wanted to create
    8300             :  * in a given refresh operation and all of the other information
    8301             :  * we learned or created in the /refresh/reveal step.
    8302             :  *
    8303             :  * @param cls the @e cls of this struct with the plugin-specific state
    8304             :  * @param melt_serial_id row ID of the commitment / melt operation in refresh_commitments
    8305             :  * @param num_rrcs number of coins to generate, size of the @a rrcs array
    8306             :  * @param rrcs information about the new coins
    8307             :  * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1
    8308             :  * @param tprivs transfer private keys to store
    8309             :  * @param tp public key to store
    8310             :  * @return query status for the transaction
    8311             :  */
    8312             : static enum GNUNET_DB_QueryStatus
    8313           0 : postgres_insert_refresh_reveal (
    8314             :   void *cls,
    8315             :   uint64_t melt_serial_id,
    8316             :   uint32_t num_rrcs,
    8317             :   const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
    8318             :   unsigned int num_tprivs,
    8319             :   const struct TALER_TransferPrivateKeyP *tprivs,
    8320             :   const struct TALER_TransferPublicKeyP *tp)
    8321             : {
    8322           0 :   struct PostgresClosure *pg = cls;
    8323             : 
    8324           0 :   if (TALER_CNC_KAPPA != num_tprivs + 1)
    8325             :   {
    8326           0 :     GNUNET_break (0);
    8327           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    8328             :   }
    8329           0 :   for (uint32_t i = 0; i<num_rrcs; i++)
    8330             :   {
    8331           0 :     const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
    8332           0 :     struct GNUNET_PQ_QueryParam params[] = {
    8333           0 :       GNUNET_PQ_query_param_uint64 (&melt_serial_id),
    8334           0 :       GNUNET_PQ_query_param_uint32 (&i),
    8335           0 :       GNUNET_PQ_query_param_auto_from_type (&rrc->orig_coin_link_sig),
    8336           0 :       GNUNET_PQ_query_param_auto_from_type (&rrc->h_denom_pub),
    8337           0 :       TALER_PQ_query_param_blinded_planchet (&rrc->blinded_planchet),
    8338           0 :       TALER_PQ_query_param_exchange_withdraw_values (&rrc->exchange_vals),
    8339           0 :       GNUNET_PQ_query_param_auto_from_type (&rrc->coin_envelope_hash),
    8340           0 :       TALER_PQ_query_param_blinded_denom_sig (&rrc->coin_sig),
    8341             :       GNUNET_PQ_query_param_end
    8342             :     };
    8343             :     enum GNUNET_DB_QueryStatus qs;
    8344             : 
    8345           0 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    8346             :                                              "insert_refresh_revealed_coin",
    8347             :                                              params);
    8348           0 :     if (0 > qs)
    8349           0 :       return qs;
    8350             :   }
    8351             : 
    8352             :   {
    8353           0 :     struct GNUNET_PQ_QueryParam params[] = {
    8354           0 :       GNUNET_PQ_query_param_uint64 (&melt_serial_id),
    8355           0 :       GNUNET_PQ_query_param_auto_from_type (tp),
    8356           0 :       GNUNET_PQ_query_param_fixed_size (
    8357             :         tprivs,
    8358             :         num_tprivs * sizeof (struct TALER_TransferPrivateKeyP)),
    8359             :       GNUNET_PQ_query_param_end
    8360             :     };
    8361             : 
    8362           0 :     return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    8363             :                                                "insert_refresh_transfer_keys",
    8364             :                                                params);
    8365             :   }
    8366             : }
    8367             : 
    8368             : 
    8369             : /**
    8370             :  * Context where we aggregate data from the database.
    8371             :  * Closure for #add_revealed_coins().
    8372             :  */
    8373             : struct GetRevealContext
    8374             : {
    8375             :   /**
    8376             :    * Array of revealed coins we obtained from the DB.
    8377             :    */
    8378             :   struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs;
    8379             : 
    8380             :   /**
    8381             :    * Length of the @a rrcs array.
    8382             :    */
    8383             :   unsigned int rrcs_len;
    8384             : 
    8385             :   /**
    8386             :    * Set to an error code if we ran into trouble.
    8387             :    */
    8388             :   enum GNUNET_DB_QueryStatus qs;
    8389             : };
    8390             : 
    8391             : 
    8392             : /**
    8393             :  * Function to be called with the results of a SELECT statement
    8394             :  * that has returned @a num_results results.
    8395             :  *
    8396             :  * @param cls closure of type `struct GetRevealContext`
    8397             :  * @param result the postgres result
    8398             :  * @param num_results the number of results in @a result
    8399             :  */
    8400             : static void
    8401           0 : add_revealed_coins (void *cls,
    8402             :                     PGresult *result,
    8403             :                     unsigned int num_results)
    8404             : {
    8405           0 :   struct GetRevealContext *grctx = cls;
    8406             : 
    8407           0 :   if (0 == num_results)
    8408           0 :     return;
    8409           0 :   grctx->rrcs = GNUNET_new_array (num_results,
    8410             :                                   struct TALER_EXCHANGEDB_RefreshRevealedCoin);
    8411           0 :   grctx->rrcs_len = num_results;
    8412           0 :   for (unsigned int i = 0; i < num_results; i++)
    8413             :   {
    8414             :     uint32_t off;
    8415           0 :     struct GNUNET_PQ_ResultSpec rso[] = {
    8416           0 :       GNUNET_PQ_result_spec_uint32 ("freshcoin_index",
    8417             :                                     &off),
    8418             :       GNUNET_PQ_result_spec_end
    8419             :     };
    8420             : 
    8421           0 :     if (GNUNET_OK !=
    8422           0 :         GNUNET_PQ_extract_result (result,
    8423             :                                   rso,
    8424             :                                   i))
    8425             :     {
    8426           0 :       GNUNET_break (0);
    8427           0 :       grctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    8428           0 :       return;
    8429             :     }
    8430           0 :     if (off >= num_results)
    8431             :     {
    8432           0 :       GNUNET_break (0);
    8433           0 :       grctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    8434           0 :       return;
    8435             :     }
    8436             :     {
    8437           0 :       struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &grctx->rrcs[off];
    8438           0 :       struct GNUNET_PQ_ResultSpec rsi[] = {
    8439             :         /* NOTE: freshcoin_index selected and discarded here... */
    8440           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    8441             :                                               &rrc->h_denom_pub),
    8442           0 :         GNUNET_PQ_result_spec_auto_from_type ("link_sig",
    8443             :                                               &rrc->orig_coin_link_sig),
    8444           0 :         GNUNET_PQ_result_spec_auto_from_type ("h_coin_ev",
    8445             :                                               &rrc->coin_envelope_hash),
    8446           0 :         TALER_PQ_result_spec_blinded_planchet ("coin_ev",
    8447             :                                                &rrc->blinded_planchet),
    8448           0 :         TALER_PQ_result_spec_exchange_withdraw_values ("ewv",
    8449             :                                                        &rrc->exchange_vals),
    8450           0 :         TALER_PQ_result_spec_blinded_denom_sig ("ev_sig",
    8451             :                                                 &rrc->coin_sig),
    8452             :         GNUNET_PQ_result_spec_end
    8453             :       };
    8454             : 
    8455           0 :       if (TALER_DENOMINATION_INVALID != rrc->blinded_planchet.cipher)
    8456             :       {
    8457             :         /* duplicate offset, not allowed */
    8458           0 :         GNUNET_break (0);
    8459           0 :         grctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    8460           0 :         return;
    8461             :       }
    8462           0 :       if (GNUNET_OK !=
    8463           0 :           GNUNET_PQ_extract_result (result,
    8464             :                                     rsi,
    8465             :                                     i))
    8466             :       {
    8467           0 :         GNUNET_break (0);
    8468           0 :         grctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    8469           0 :         return;
    8470             :       }
    8471             :     }
    8472             :   }
    8473             : }
    8474             : 
    8475             : 
    8476             : /**
    8477             :  * Lookup in the database the coins that we want to
    8478             :  * create in the given refresh operation.
    8479             :  *
    8480             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    8481             :  * @param rc identify commitment and thus refresh operation
    8482             :  * @param cb function to call with the results
    8483             :  * @param cb_cls closure for @a cb
    8484             :  * @return transaction status
    8485             :  */
    8486             : static enum GNUNET_DB_QueryStatus
    8487           0 : postgres_get_refresh_reveal (void *cls,
    8488             :                              const struct TALER_RefreshCommitmentP *rc,
    8489             :                              TALER_EXCHANGEDB_RefreshCallback cb,
    8490             :                              void *cb_cls)
    8491             : {
    8492           0 :   struct PostgresClosure *pg = cls;
    8493             :   struct GetRevealContext grctx;
    8494             :   enum GNUNET_DB_QueryStatus qs;
    8495           0 :   struct GNUNET_PQ_QueryParam params[] = {
    8496           0 :     GNUNET_PQ_query_param_auto_from_type (rc),
    8497             :     GNUNET_PQ_query_param_end
    8498             :   };
    8499             : 
    8500           0 :   memset (&grctx,
    8501             :           0,
    8502             :           sizeof (grctx));
    8503           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    8504             :                                              "get_refresh_revealed_coins",
    8505             :                                              params,
    8506             :                                              &add_revealed_coins,
    8507             :                                              &grctx);
    8508           0 :   switch (qs)
    8509             :   {
    8510           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
    8511             :   case GNUNET_DB_STATUS_SOFT_ERROR:
    8512             :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    8513           0 :     goto cleanup;
    8514           0 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    8515             :   default: /* can have more than one result */
    8516           0 :     break;
    8517             :   }
    8518           0 :   switch (grctx.qs)
    8519             :   {
    8520           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
    8521             :   case GNUNET_DB_STATUS_SOFT_ERROR:
    8522           0 :     goto cleanup;
    8523           0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    8524             :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: /* should be impossible */
    8525           0 :     break;
    8526             :   }
    8527             : 
    8528             :   /* Pass result back to application */
    8529           0 :   cb (cb_cls,
    8530             :       grctx.rrcs_len,
    8531           0 :       grctx.rrcs);
    8532           0 : cleanup:
    8533           0 :   for (unsigned int i = 0; i < grctx.rrcs_len; i++)
    8534             :   {
    8535           0 :     struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &grctx.rrcs[i];
    8536             : 
    8537           0 :     TALER_blinded_denom_sig_free (&rrc->coin_sig);
    8538           0 :     TALER_blinded_planchet_free (&rrc->blinded_planchet);
    8539             :   }
    8540           0 :   GNUNET_free (grctx.rrcs);
    8541           0 :   return qs;
    8542             : }
    8543             : 
    8544             : 
    8545             : /**
    8546             :  * Closure for #add_ldl().
    8547             :  */
    8548             : struct LinkDataContext
    8549             : {
    8550             :   /**
    8551             :    * Function to call on each result.
    8552             :    */
    8553             :   TALER_EXCHANGEDB_LinkCallback ldc;
    8554             : 
    8555             :   /**
    8556             :    * Closure for @e ldc.
    8557             :    */
    8558             :   void *ldc_cls;
    8559             : 
    8560             :   /**
    8561             :    * Last transfer public key for which we have information in @e last.
    8562             :    * Only valid if @e last is non-NULL.
    8563             :    */
    8564             :   struct TALER_TransferPublicKeyP transfer_pub;
    8565             : 
    8566             :   /**
    8567             :    * Link data for @e transfer_pub
    8568             :    */
    8569             :   struct TALER_EXCHANGEDB_LinkList *last;
    8570             : 
    8571             :   /**
    8572             :    * Status, set to #GNUNET_SYSERR on errors,
    8573             :    */
    8574             :   int status;
    8575             : };
    8576             : 
    8577             : 
    8578             : /**
    8579             :  * Free memory of the link data list.
    8580             :  *
    8581             :  * @param cls the @e cls of this struct with the plugin-specific state (unused)
    8582             :  * @param ldl link data list to release
    8583             :  */
    8584             : static void
    8585           0 : free_link_data_list (void *cls,
    8586             :                      struct TALER_EXCHANGEDB_LinkList *ldl)
    8587             : {
    8588             :   struct TALER_EXCHANGEDB_LinkList *next;
    8589             : 
    8590             :   (void) cls;
    8591           0 :   while (NULL != ldl)
    8592             :   {
    8593           0 :     next = ldl->next;
    8594           0 :     TALER_denom_pub_free (&ldl->denom_pub);
    8595           0 :     TALER_blinded_denom_sig_free (&ldl->ev_sig);
    8596           0 :     GNUNET_free (ldl);
    8597           0 :     ldl = next;
    8598             :   }
    8599           0 : }
    8600             : 
    8601             : 
    8602             : /**
    8603             :  * Function to be called with the results of a SELECT statement
    8604             :  * that has returned @a num_results results.
    8605             :  *
    8606             :  * @param cls closure of type `struct LinkDataContext *`
    8607             :  * @param result the postgres result
    8608             :  * @param num_results the number of results in @a result
    8609             :  */
    8610             : static void
    8611           0 : add_ldl (void *cls,
    8612             :          PGresult *result,
    8613             :          unsigned int num_results)
    8614             : {
    8615           0 :   struct LinkDataContext *ldctx = cls;
    8616             : 
    8617           0 :   for (int i = num_results - 1; i >= 0; i--)
    8618             :   {
    8619             :     struct TALER_EXCHANGEDB_LinkList *pos;
    8620             :     struct TALER_TransferPublicKeyP transfer_pub;
    8621             : 
    8622           0 :     pos = GNUNET_new (struct TALER_EXCHANGEDB_LinkList);
    8623             :     {
    8624             :       struct TALER_BlindedPlanchet bp;
    8625           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    8626           0 :         GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
    8627             :                                               &transfer_pub),
    8628           0 :         GNUNET_PQ_result_spec_auto_from_type ("link_sig",
    8629             :                                               &pos->orig_coin_link_sig),
    8630           0 :         TALER_PQ_result_spec_blinded_denom_sig ("ev_sig",
    8631             :                                                 &pos->ev_sig),
    8632           0 :         GNUNET_PQ_result_spec_uint32 ("freshcoin_index",
    8633             :                                       &pos->coin_refresh_offset),
    8634           0 :         TALER_PQ_result_spec_exchange_withdraw_values ("ewv",
    8635             :                                                        &pos->alg_values),
    8636           0 :         TALER_PQ_result_spec_denom_pub ("denom_pub",
    8637             :                                         &pos->denom_pub),
    8638           0 :         TALER_PQ_result_spec_blinded_planchet ("coin_ev",
    8639             :                                                &bp),
    8640             :         GNUNET_PQ_result_spec_end
    8641             :       };
    8642             : 
    8643           0 :       if (GNUNET_OK !=
    8644           0 :           GNUNET_PQ_extract_result (result,
    8645             :                                     rs,
    8646             :                                     i))
    8647             :       {
    8648           0 :         GNUNET_break (0);
    8649           0 :         GNUNET_free (pos);
    8650           0 :         ldctx->status = GNUNET_SYSERR;
    8651           0 :         return;
    8652             :       }
    8653           0 :       if (TALER_DENOMINATION_CS == bp.cipher)
    8654             :       {
    8655           0 :         pos->nonce = bp.details.cs_blinded_planchet.nonce;
    8656           0 :         pos->have_nonce = true;
    8657             :       }
    8658           0 :       TALER_blinded_planchet_free (&bp);
    8659             :     }
    8660           0 :     if ( (NULL != ldctx->last) &&
    8661           0 :          (0 == GNUNET_memcmp (&transfer_pub,
    8662             :                               &ldctx->transfer_pub)) )
    8663             :     {
    8664           0 :       pos->next = ldctx->last;
    8665             :     }
    8666             :     else
    8667             :     {
    8668           0 :       if (NULL != ldctx->last)
    8669             :       {
    8670           0 :         ldctx->ldc (ldctx->ldc_cls,
    8671           0 :                     &ldctx->transfer_pub,
    8672           0 :                     ldctx->last);
    8673           0 :         free_link_data_list (cls,
    8674             :                              ldctx->last);
    8675             :       }
    8676           0 :       ldctx->transfer_pub = transfer_pub;
    8677             :     }
    8678           0 :     ldctx->last = pos;
    8679             :   }
    8680             : }
    8681             : 
    8682             : 
    8683             : /**
    8684             :  * Obtain the link data of a coin, that is the encrypted link
    8685             :  * information, the denomination keys and the signatures.
    8686             :  *
    8687             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    8688             :  * @param coin_pub public key of the coin
    8689             :  * @param ldc function to call for each session the coin was melted into
    8690             :  * @param ldc_cls closure for @a tdc
    8691             :  * @return transaction status code
    8692             :  */
    8693             : static enum GNUNET_DB_QueryStatus
    8694           0 : postgres_get_link_data (void *cls,
    8695             :                         const struct TALER_CoinSpendPublicKeyP *coin_pub,
    8696             :                         TALER_EXCHANGEDB_LinkCallback ldc,
    8697             :                         void *ldc_cls)
    8698             : {
    8699           0 :   struct PostgresClosure *pg = cls;
    8700           0 :   struct GNUNET_PQ_QueryParam params[] = {
    8701           0 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    8702             :     GNUNET_PQ_query_param_end
    8703             :   };
    8704             :   enum GNUNET_DB_QueryStatus qs;
    8705             :   struct LinkDataContext ldctx;
    8706             : 
    8707           0 :   ldctx.ldc = ldc;
    8708           0 :   ldctx.ldc_cls = ldc_cls;
    8709           0 :   ldctx.last = NULL;
    8710           0 :   ldctx.status = GNUNET_OK;
    8711           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    8712             :                                              "get_link",
    8713             :                                              params,
    8714             :                                              &add_ldl,
    8715             :                                              &ldctx);
    8716           0 :   if (NULL != ldctx.last)
    8717             :   {
    8718           0 :     if (GNUNET_OK == ldctx.status)
    8719             :     {
    8720             :       /* call callback one more time! */
    8721           0 :       ldc (ldc_cls,
    8722             :            &ldctx.transfer_pub,
    8723           0 :            ldctx.last);
    8724             :     }
    8725           0 :     free_link_data_list (cls,
    8726             :                          ldctx.last);
    8727           0 :     ldctx.last = NULL;
    8728             :   }
    8729           0 :   if (GNUNET_OK != ldctx.status)
    8730           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    8731           0 :   return qs;
    8732             : }
    8733             : 
    8734             : 
    8735             : /**
    8736             :  * Closure for callbacks called from #postgres_get_coin_transactions()
    8737             :  */
    8738             : struct CoinHistoryContext
    8739             : {
    8740             :   /**
    8741             :    * Head of the coin's history list.
    8742             :    */
    8743             :   struct TALER_EXCHANGEDB_TransactionList *head;
    8744             : 
    8745             :   /**
    8746             :    * Public key of the coin we are building the history for.
    8747             :    */
    8748             :   const struct TALER_CoinSpendPublicKeyP *coin_pub;
    8749             : 
    8750             :   /**
    8751             :    * Closure for all callbacks of this database plugin.
    8752             :    */
    8753             :   void *db_cls;
    8754             : 
    8755             :   /**
    8756             :    * Plugin context.
    8757             :    */
    8758             :   struct PostgresClosure *pg;
    8759             : 
    8760             :   /**
    8761             :    * Set to 'true' if the transaction failed.
    8762             :    */
    8763             :   bool failed;
    8764             : 
    8765             :   /**
    8766             :    * Set to 'true' if we found a deposit or melt (for invariant check).
    8767             :    */
    8768             :   bool have_deposit_or_melt;
    8769             : };
    8770             : 
    8771             : 
    8772             : /**
    8773             :  * Function to be called with the results of a SELECT statement
    8774             :  * that has returned @a num_results results.
    8775             :  *
    8776             :  * @param cls closure of type `struct CoinHistoryContext`
    8777             :  * @param result the postgres result
    8778             :  * @param num_results the number of results in @a result
    8779             :  */
    8780             : static void
    8781           0 : add_coin_deposit (void *cls,
    8782             :                   PGresult *result,
    8783             :                   unsigned int num_results)
    8784             : {
    8785           0 :   struct CoinHistoryContext *chc = cls;
    8786           0 :   struct PostgresClosure *pg = chc->pg;
    8787             : 
    8788           0 :   for (unsigned int i = 0; i < num_results; i++)
    8789             :   {
    8790             :     struct TALER_EXCHANGEDB_DepositListEntry *deposit;
    8791             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    8792             :     uint64_t serial_id;
    8793             : 
    8794           0 :     chc->have_deposit_or_melt = true;
    8795           0 :     deposit = GNUNET_new (struct TALER_EXCHANGEDB_DepositListEntry);
    8796             :     {
    8797           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    8798           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    8799             :                                      &deposit->amount_with_fee),
    8800           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    8801             :                                      &deposit->deposit_fee),
    8802           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    8803             :                                               &deposit->h_denom_pub),
    8804           0 :         GNUNET_PQ_result_spec_allow_null (
    8805           0 :           GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
    8806             :                                                 &deposit->h_age_commitment),
    8807             :           &deposit->no_age_commitment),
    8808           0 :         GNUNET_PQ_result_spec_timestamp ("wallet_timestamp",
    8809             :                                          &deposit->timestamp),
    8810           0 :         GNUNET_PQ_result_spec_timestamp ("refund_deadline",
    8811             :                                          &deposit->refund_deadline),
    8812           0 :         GNUNET_PQ_result_spec_timestamp ("wire_deadline",
    8813             :                                          &deposit->wire_deadline),
    8814           0 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    8815             :                                               &deposit->merchant_pub),
    8816           0 :         GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    8817             :                                               &deposit->h_contract_terms),
    8818           0 :         GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
    8819             :                                               &deposit->wire_salt),
    8820           0 :         GNUNET_PQ_result_spec_string ("payto_uri",
    8821             :                                       &deposit->receiver_wire_account),
    8822           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    8823             :                                               &deposit->csig),
    8824           0 :         GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
    8825             :                                       &serial_id),
    8826           0 :         GNUNET_PQ_result_spec_auto_from_type ("done",
    8827             :                                               &deposit->done),
    8828             :         GNUNET_PQ_result_spec_end
    8829             :       };
    8830             : 
    8831           0 :       if (GNUNET_OK !=
    8832           0 :           GNUNET_PQ_extract_result (result,
    8833             :                                     rs,
    8834             :                                     i))
    8835             :       {
    8836           0 :         GNUNET_break (0);
    8837           0 :         GNUNET_free (deposit);
    8838           0 :         chc->failed = true;
    8839           0 :         return;
    8840             :       }
    8841             :     }
    8842           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    8843           0 :     tl->next = chc->head;
    8844           0 :     tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
    8845           0 :     tl->details.deposit = deposit;
    8846           0 :     tl->serial_id = serial_id;
    8847           0 :     chc->head = tl;
    8848             :   }
    8849             : }
    8850             : 
    8851             : 
    8852             : /**
    8853             :  * Function to be called with the results of a SELECT statement
    8854             :  * that has returned @a num_results results.
    8855             :  *
    8856             :  * @param cls closure of type `struct CoinHistoryContext`
    8857             :  * @param result the postgres result
    8858             :  * @param num_results the number of results in @a result
    8859             :  */
    8860             : static void
    8861           0 : add_coin_purse_deposit (void *cls,
    8862             :                         PGresult *result,
    8863             :                         unsigned int num_results)
    8864             : {
    8865           0 :   struct CoinHistoryContext *chc = cls;
    8866           0 :   struct PostgresClosure *pg = chc->pg;
    8867             : 
    8868           0 :   for (unsigned int i = 0; i < num_results; i++)
    8869             :   {
    8870             :     struct TALER_EXCHANGEDB_PurseDepositListEntry *deposit;
    8871             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    8872             :     uint64_t serial_id;
    8873             : 
    8874           0 :     chc->have_deposit_or_melt = true;
    8875           0 :     deposit = GNUNET_new (struct TALER_EXCHANGEDB_PurseDepositListEntry);
    8876             :     {
    8877           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    8878           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    8879             :                                      &deposit->amount),
    8880           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    8881             :                                      &deposit->deposit_fee),
    8882           0 :         GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
    8883             :                                               &deposit->purse_pub),
    8884           0 :         GNUNET_PQ_result_spec_uint64 ("purse_deposit_serial_id",
    8885             :                                       &serial_id),
    8886           0 :         GNUNET_PQ_result_spec_allow_null (
    8887             :           GNUNET_PQ_result_spec_string ("partner_base_url",
    8888             :                                         &deposit->exchange_base_url),
    8889             :           NULL),
    8890           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    8891             :                                               &deposit->coin_sig),
    8892           0 :         GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
    8893             :                                               &deposit->h_age_commitment),
    8894           0 :         GNUNET_PQ_result_spec_bool ("refunded",
    8895             :                                     &deposit->refunded),
    8896             :         GNUNET_PQ_result_spec_end
    8897             :       };
    8898             : 
    8899           0 :       if (GNUNET_OK !=
    8900           0 :           GNUNET_PQ_extract_result (result,
    8901             :                                     rs,
    8902             :                                     i))
    8903             :       {
    8904           0 :         GNUNET_break (0);
    8905           0 :         GNUNET_free (deposit);
    8906           0 :         chc->failed = true;
    8907           0 :         return;
    8908             :       }
    8909           0 :       deposit->no_age_commitment = GNUNET_is_zero (&deposit->h_age_commitment);
    8910             :     }
    8911           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    8912           0 :     tl->next = chc->head;
    8913           0 :     tl->type = TALER_EXCHANGEDB_TT_PURSE_DEPOSIT;
    8914           0 :     tl->details.purse_deposit = deposit;
    8915           0 :     tl->serial_id = serial_id;
    8916           0 :     chc->head = tl;
    8917             :   }
    8918             : }
    8919             : 
    8920             : 
    8921             : /**
    8922             :  * Function to be called with the results of a SELECT statement
    8923             :  * that has returned @a num_results results.
    8924             :  *
    8925             :  * @param cls closure of type `struct CoinHistoryContext`
    8926             :  * @param result the postgres result
    8927             :  * @param num_results the number of results in @a result
    8928             :  */
    8929             : static void
    8930           0 : add_coin_melt (void *cls,
    8931             :                PGresult *result,
    8932             :                unsigned int num_results)
    8933             : {
    8934           0 :   struct CoinHistoryContext *chc = cls;
    8935           0 :   struct PostgresClosure *pg = chc->pg;
    8936             : 
    8937           0 :   for (unsigned int i = 0; i<num_results; i++)
    8938             :   {
    8939             :     struct TALER_EXCHANGEDB_MeltListEntry *melt;
    8940             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    8941             :     uint64_t serial_id;
    8942             : 
    8943           0 :     chc->have_deposit_or_melt = true;
    8944           0 :     melt = GNUNET_new (struct TALER_EXCHANGEDB_MeltListEntry);
    8945             :     {
    8946           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    8947           0 :         GNUNET_PQ_result_spec_auto_from_type ("rc",
    8948             :                                               &melt->rc),
    8949             :         /* oldcoin_index not needed */
    8950           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    8951             :                                               &melt->h_denom_pub),
    8952           0 :         GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
    8953             :                                               &melt->coin_sig),
    8954           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    8955             :                                      &melt->amount_with_fee),
    8956           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
    8957             :                                      &melt->melt_fee),
    8958           0 :         GNUNET_PQ_result_spec_allow_null (
    8959           0 :           GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
    8960             :                                                 &melt->h_age_commitment),
    8961             :           &melt->no_age_commitment),
    8962           0 :         GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
    8963             :                                       &serial_id),
    8964             :         GNUNET_PQ_result_spec_end
    8965             :       };
    8966             : 
    8967           0 :       if (GNUNET_OK !=
    8968           0 :           GNUNET_PQ_extract_result (result,
    8969             :                                     rs,
    8970             :                                     i))
    8971             :       {
    8972           0 :         GNUNET_break (0);
    8973           0 :         GNUNET_free (melt);
    8974           0 :         chc->failed = true;
    8975           0 :         return;
    8976             :       }
    8977             :     }
    8978           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    8979           0 :     tl->next = chc->head;
    8980           0 :     tl->type = TALER_EXCHANGEDB_TT_MELT;
    8981           0 :     tl->details.melt = melt;
    8982           0 :     tl->serial_id = serial_id;
    8983           0 :     chc->head = tl;
    8984             :   }
    8985             : }
    8986             : 
    8987             : 
    8988             : /**
    8989             :  * Function to be called with the results of a SELECT statement
    8990             :  * that has returned @a num_results results.
    8991             :  *
    8992             :  * @param cls closure of type `struct CoinHistoryContext`
    8993             :  * @param result the postgres result
    8994             :  * @param num_results the number of results in @a result
    8995             :  */
    8996             : static void
    8997           0 : add_coin_refund (void *cls,
    8998             :                  PGresult *result,
    8999             :                  unsigned int num_results)
    9000             : {
    9001           0 :   struct CoinHistoryContext *chc = cls;
    9002           0 :   struct PostgresClosure *pg = chc->pg;
    9003             : 
    9004           0 :   for (unsigned int i = 0; i<num_results; i++)
    9005             :   {
    9006             :     struct TALER_EXCHANGEDB_RefundListEntry *refund;
    9007             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    9008             :     uint64_t serial_id;
    9009             : 
    9010           0 :     refund = GNUNET_new (struct TALER_EXCHANGEDB_RefundListEntry);
    9011             :     {
    9012           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    9013           0 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    9014             :                                               &refund->merchant_pub),
    9015           0 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
    9016             :                                               &refund->merchant_sig),
    9017           0 :         GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    9018             :                                               &refund->h_contract_terms),
    9019           0 :         GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
    9020             :                                       &refund->rtransaction_id),
    9021           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    9022             :                                      &refund->refund_amount),
    9023           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
    9024             :                                      &refund->refund_fee),
    9025           0 :         GNUNET_PQ_result_spec_uint64 ("refund_serial_id",
    9026             :                                       &serial_id),
    9027             :         GNUNET_PQ_result_spec_end
    9028             :       };
    9029             : 
    9030           0 :       if (GNUNET_OK !=
    9031           0 :           GNUNET_PQ_extract_result (result,
    9032             :                                     rs,
    9033             :                                     i))
    9034             :       {
    9035           0 :         GNUNET_break (0);
    9036           0 :         GNUNET_free (refund);
    9037           0 :         chc->failed = true;
    9038           0 :         return;
    9039             :       }
    9040             :     }
    9041           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    9042           0 :     tl->next = chc->head;
    9043           0 :     tl->type = TALER_EXCHANGEDB_TT_REFUND;
    9044           0 :     tl->details.refund = refund;
    9045           0 :     tl->serial_id = serial_id;
    9046           0 :     chc->head = tl;
    9047             :   }
    9048             : }
    9049             : 
    9050             : 
    9051             : /**
    9052             :  * Function to be called with the results of a SELECT statement
    9053             :  * that has returned @a num_results results.
    9054             :  *
    9055             :  * @param cls closure of type `struct CoinHistoryContext`
    9056             :  * @param result the postgres result
    9057             :  * @param num_results the number of results in @a result
    9058             :  */
    9059             : static void
    9060           0 : add_old_coin_recoup (void *cls,
    9061             :                      PGresult *result,
    9062             :                      unsigned int num_results)
    9063             : {
    9064           0 :   struct CoinHistoryContext *chc = cls;
    9065           0 :   struct PostgresClosure *pg = chc->pg;
    9066             : 
    9067           0 :   for (unsigned int i = 0; i<num_results; i++)
    9068             :   {
    9069             :     struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
    9070             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    9071             :     uint64_t serial_id;
    9072             : 
    9073           0 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
    9074             :     {
    9075           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    9076           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    9077             :                                               &recoup->coin.coin_pub),
    9078           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    9079             :                                               &recoup->coin_sig),
    9080           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
    9081             :                                               &recoup->coin_blind),
    9082           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    9083             :                                      &recoup->value),
    9084           0 :         GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
    9085             :                                          &recoup->timestamp),
    9086           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    9087             :                                               &recoup->coin.denom_pub_hash),
    9088           0 :         TALER_PQ_result_spec_denom_sig ("denom_sig",
    9089             :                                         &recoup->coin.denom_sig),
    9090           0 :         GNUNET_PQ_result_spec_uint64 ("recoup_refresh_uuid",
    9091             :                                       &serial_id),
    9092             :         GNUNET_PQ_result_spec_end
    9093             :       };
    9094             : 
    9095           0 :       if (GNUNET_OK !=
    9096           0 :           GNUNET_PQ_extract_result (result,
    9097             :                                     rs,
    9098             :                                     i))
    9099             :       {
    9100           0 :         GNUNET_break (0);
    9101           0 :         GNUNET_free (recoup);
    9102           0 :         chc->failed = true;
    9103           0 :         return;
    9104             :       }
    9105           0 :       recoup->old_coin_pub = *chc->coin_pub;
    9106             :     }
    9107           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    9108           0 :     tl->next = chc->head;
    9109           0 :     tl->type = TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP;
    9110           0 :     tl->details.old_coin_recoup = recoup;
    9111           0 :     tl->serial_id = serial_id;
    9112           0 :     chc->head = tl;
    9113             :   }
    9114             : }
    9115             : 
    9116             : 
    9117             : /**
    9118             :  * Function to be called with the results of a SELECT statement
    9119             :  * that has returned @a num_results results.
    9120             :  *
    9121             :  * @param cls closure of type `struct CoinHistoryContext`
    9122             :  * @param result the postgres result
    9123             :  * @param num_results the number of results in @a result
    9124             :  */
    9125             : static void
    9126           0 : add_coin_recoup (void *cls,
    9127             :                  PGresult *result,
    9128             :                  unsigned int num_results)
    9129             : {
    9130           0 :   struct CoinHistoryContext *chc = cls;
    9131           0 :   struct PostgresClosure *pg = chc->pg;
    9132             : 
    9133           0 :   for (unsigned int i = 0; i<num_results; i++)
    9134             :   {
    9135             :     struct TALER_EXCHANGEDB_RecoupListEntry *recoup;
    9136             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    9137             :     uint64_t serial_id;
    9138             : 
    9139           0 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupListEntry);
    9140             :     {
    9141           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    9142           0 :         GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    9143             :                                               &recoup->reserve_pub),
    9144           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    9145             :                                               &recoup->coin_sig),
    9146           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    9147             :                                               &recoup->h_denom_pub),
    9148           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
    9149             :                                               &recoup->coin_blind),
    9150           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    9151             :                                      &recoup->value),
    9152           0 :         GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
    9153             :                                          &recoup->timestamp),
    9154           0 :         GNUNET_PQ_result_spec_uint64 ("recoup_uuid",
    9155             :                                       &serial_id),
    9156             :         GNUNET_PQ_result_spec_end
    9157             :       };
    9158             : 
    9159           0 :       if (GNUNET_OK !=
    9160           0 :           GNUNET_PQ_extract_result (result,
    9161             :                                     rs,
    9162             :                                     i))
    9163             :       {
    9164           0 :         GNUNET_break (0);
    9165           0 :         GNUNET_free (recoup);
    9166           0 :         chc->failed = true;
    9167           0 :         return;
    9168             :       }
    9169             :     }
    9170           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    9171           0 :     tl->next = chc->head;
    9172           0 :     tl->type = TALER_EXCHANGEDB_TT_RECOUP;
    9173           0 :     tl->details.recoup = recoup;
    9174           0 :     tl->serial_id = serial_id;
    9175           0 :     chc->head = tl;
    9176             :   }
    9177             : }
    9178             : 
    9179             : 
    9180             : /**
    9181             :  * Function to be called with the results of a SELECT statement
    9182             :  * that has returned @a num_results results.
    9183             :  *
    9184             :  * @param cls closure of type `struct CoinHistoryContext`
    9185             :  * @param result the postgres result
    9186             :  * @param num_results the number of results in @a result
    9187             :  */
    9188             : static void
    9189           0 : add_coin_recoup_refresh (void *cls,
    9190             :                          PGresult *result,
    9191             :                          unsigned int num_results)
    9192             : {
    9193           0 :   struct CoinHistoryContext *chc = cls;
    9194           0 :   struct PostgresClosure *pg = chc->pg;
    9195             : 
    9196           0 :   for (unsigned int i = 0; i<num_results; i++)
    9197             :   {
    9198             :     struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
    9199             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    9200             :     uint64_t serial_id;
    9201             : 
    9202           0 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
    9203             :     {
    9204           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
    9205           0 :         GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
    9206             :                                               &recoup->old_coin_pub),
    9207           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    9208             :                                               &recoup->coin_sig),
    9209           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
    9210             :                                               &recoup->coin_blind),
    9211           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    9212             :                                      &recoup->value),
    9213           0 :         GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
    9214             :                                          &recoup->timestamp),
    9215           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    9216             :                                               &recoup->coin.denom_pub_hash),
    9217           0 :         TALER_PQ_result_spec_denom_sig ("denom_sig",
    9218             :                                         &recoup->coin.denom_sig),
    9219           0 :         GNUNET_PQ_result_spec_uint64 ("recoup_refresh_uuid",
    9220             :                                       &serial_id),
    9221             :         GNUNET_PQ_result_spec_end
    9222             :       };
    9223             : 
    9224           0 :       if (GNUNET_OK !=
    9225           0 :           GNUNET_PQ_extract_result (result,
    9226             :                                     rs,
    9227             :                                     i))
    9228             :       {
    9229           0 :         GNUNET_break (0);
    9230           0 :         GNUNET_free (recoup);
    9231           0 :         chc->failed = true;
    9232           0 :         return;
    9233             :       }
    9234           0 :       recoup->coin.coin_pub = *chc->coin_pub;
    9235             :     }
    9236           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    9237           0 :     tl->next = chc->head;
    9238           0 :     tl->type = TALER_EXCHANGEDB_TT_RECOUP_REFRESH;
    9239           0 :     tl->details.recoup_refresh = recoup;
    9240           0 :     tl->serial_id = serial_id;
    9241           0 :     chc->head = tl;
    9242             :   }
    9243             : }
    9244             : 
    9245             : 
    9246             : /**
    9247             :  * Work we need to do.
    9248             :  */
    9249             : struct Work
    9250             : {
    9251             :   /**
    9252             :    * SQL prepared statement name.
    9253             :    */
    9254             :   const char *statement;
    9255             : 
    9256             :   /**
    9257             :    * Function to call to handle the result(s).
    9258             :    */
    9259             :   GNUNET_PQ_PostgresResultHandler cb;
    9260             : };
    9261             : 
    9262             : 
    9263             : /**
    9264             :  * Compile a list of all (historic) transactions performed with the given coin
    9265             :  * (/refresh/melt, /deposit, /refund and /recoup operations).
    9266             :  *
    9267             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    9268             :  * @param coin_pub coin to investigate
    9269             :  * @param[out] tlp set to list of transactions, NULL if coin is fresh
    9270             :  * @return database transaction status
    9271             :  */
    9272             : static enum GNUNET_DB_QueryStatus
    9273           0 : postgres_get_coin_transactions (
    9274             :   void *cls,
    9275             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    9276             :   struct TALER_EXCHANGEDB_TransactionList **tlp)
    9277             : {
    9278           0 :   struct PostgresClosure *pg = cls;
    9279             :   static const struct Work work[] = {
    9280             :     /** #TALER_EXCHANGEDB_TT_DEPOSIT */
    9281             :     { "get_deposit_with_coin_pub",
    9282             :       &add_coin_deposit },
    9283             :     /** #TALER_EXCHANGEDB_TT_MELT */
    9284             :     { "get_refresh_session_by_coin",
    9285             :       &add_coin_melt },
    9286             :     /** #TALER_EXCHANGEDB_TT_PURSE_DEPOSIT */
    9287             :     { "get_purse_deposit_by_coin_pub",
    9288             :       &add_coin_purse_deposit },
    9289             :     /** #TALER_EXCHANGEDB_TT_REFUND */
    9290             :     { "get_refunds_by_coin",
    9291             :       &add_coin_refund },
    9292             :     /** #TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP */
    9293             :     { "recoup_by_old_coin",
    9294             :       &add_old_coin_recoup },
    9295             :     /** #TALER_EXCHANGEDB_TT_RECOUP */
    9296             :     { "recoup_by_coin",
    9297             :       &add_coin_recoup },
    9298             :     /** #TALER_EXCHANGEDB_TT_RECOUP_REFRESH */
    9299             :     { "recoup_by_refreshed_coin",
    9300             :       &add_coin_recoup_refresh },
    9301             :     { NULL, NULL }
    9302             :   };
    9303           0 :   struct GNUNET_PQ_QueryParam params[] = {
    9304           0 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    9305             :     GNUNET_PQ_query_param_end
    9306             :   };
    9307             :   enum GNUNET_DB_QueryStatus qs;
    9308           0 :   struct CoinHistoryContext chc = {
    9309             :     .head = NULL,
    9310             :     .coin_pub = coin_pub,
    9311             :     .pg = pg,
    9312             :     .db_cls = cls
    9313             :   };
    9314             : 
    9315           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    9316             :               "Getting transactions for coin %s\n",
    9317             :               TALER_B2S (coin_pub));
    9318           0 :   for (unsigned int i = 0; NULL != work[i].statement; i++)
    9319             :   {
    9320           0 :     qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    9321             :                                                work[i].statement,
    9322             :                                                params,
    9323             :                                                work[i].cb,
    9324             :                                                &chc);
    9325           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    9326             :                 "Coin %s yielded %d transactions of type %s\n",
    9327             :                 TALER_B2S (coin_pub),
    9328             :                 qs,
    9329             :                 work[i].statement);
    9330           0 :     if ( (0 > qs) ||
    9331           0 :          (chc.failed) )
    9332             :     {
    9333           0 :       if (NULL != chc.head)
    9334           0 :         common_free_coin_transaction_list (cls,
    9335             :                                            chc.head);
    9336           0 :       *tlp = NULL;
    9337           0 :       if (chc.failed)
    9338           0 :         qs = GNUNET_DB_STATUS_HARD_ERROR;
    9339           0 :       return qs;
    9340             :     }
    9341             :   }
    9342           0 :   *tlp = chc.head;
    9343           0 :   if (NULL == chc.head)
    9344           0 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    9345           0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    9346             : }
    9347             : 
    9348             : 
    9349             : /**
    9350             :  * Closure for #handle_wt_result.
    9351             :  */
    9352             : struct WireTransferResultContext
    9353             : {
    9354             :   /**
    9355             :    * Function to call on each result.
    9356             :    */
    9357             :   TALER_EXCHANGEDB_AggregationDataCallback cb;
    9358             : 
    9359             :   /**
    9360             :    * Closure for @e cb.
    9361             :    */
    9362             :   void *cb_cls;
    9363             : 
    9364             :   /**
    9365             :    * Plugin context.
    9366             :    */
    9367             :   struct PostgresClosure *pg;
    9368             : 
    9369             :   /**
    9370             :    * Set to #GNUNET_SYSERR on serious errors.
    9371             :    */
    9372             :   int status;
    9373             : };
    9374             : 
    9375             : 
    9376             : /**
    9377             :  * Function to be called with the results of a SELECT statement
    9378             :  * that has returned @a num_results results.  Helper function
    9379             :  * for #postgres_lookup_wire_transfer().
    9380             :  *
    9381             :  * @param cls closure of type `struct WireTransferResultContext *`
    9382             :  * @param result the postgres result
    9383             :  * @param num_results the number of results in @a result
    9384             :  */
    9385             : static void
    9386           0 : handle_wt_result (void *cls,
    9387             :                   PGresult *result,
    9388             :                   unsigned int num_results)
    9389             : {
    9390           0 :   struct WireTransferResultContext *ctx = cls;
    9391           0 :   struct PostgresClosure *pg = ctx->pg;
    9392             : 
    9393           0 :   for (unsigned int i = 0; i<num_results; i++)
    9394             :   {
    9395             :     uint64_t rowid;
    9396             :     struct TALER_PrivateContractHashP h_contract_terms;
    9397             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    9398             :     struct TALER_PaytoHashP h_payto;
    9399             :     struct TALER_MerchantPublicKeyP merchant_pub;
    9400             :     struct GNUNET_TIME_Timestamp exec_time;
    9401             :     struct TALER_Amount amount_with_fee;
    9402             :     struct TALER_Amount deposit_fee;
    9403             :     struct TALER_DenominationPublicKey denom_pub;
    9404             :     char *payto_uri;
    9405           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
    9406           0 :       GNUNET_PQ_result_spec_uint64 ("aggregation_serial_id", &rowid),
    9407           0 :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    9408             :                                             &h_contract_terms),
    9409           0 :       GNUNET_PQ_result_spec_string ("payto_uri",
    9410             :                                     &payto_uri),
    9411           0 :       GNUNET_PQ_result_spec_auto_from_type ("wire_target_h_payto",
    9412             :                                             &h_payto),
    9413           0 :       TALER_PQ_result_spec_denom_pub ("denom_pub",
    9414             :                                       &denom_pub),
    9415           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    9416             :                                             &coin_pub),
    9417           0 :       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    9418             :                                             &merchant_pub),
    9419           0 :       GNUNET_PQ_result_spec_timestamp ("execution_date",
    9420             :                                        &exec_time),
    9421           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    9422             :                                    &amount_with_fee),
    9423           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    9424             :                                    &deposit_fee),
    9425             :       GNUNET_PQ_result_spec_end
    9426             :     };
    9427             : 
    9428           0 :     if (GNUNET_OK !=
    9429           0 :         GNUNET_PQ_extract_result (result,
    9430             :                                   rs,
    9431             :                                   i))
    9432             :     {
    9433           0 :       GNUNET_break (0);
    9434           0 :       ctx->status = GNUNET_SYSERR;
    9435           0 :       return;
    9436             :     }
    9437           0 :     ctx->cb (ctx->cb_cls,
    9438             :              rowid,
    9439             :              &merchant_pub,
    9440             :              payto_uri,
    9441             :              &h_payto,
    9442             :              exec_time,
    9443             :              &h_contract_terms,
    9444             :              &denom_pub,
    9445             :              &coin_pub,
    9446             :              &amount_with_fee,
    9447             :              &deposit_fee);
    9448           0 :     GNUNET_PQ_cleanup_result (rs);
    9449             :   }
    9450             : }
    9451             : 
    9452             : 
    9453             : /**
    9454             :  * Lookup the list of Taler transactions that were aggregated
    9455             :  * into a wire transfer by the respective @a wtid.
    9456             :  *
    9457             :  * @param cls closure
    9458             :  * @param wtid the raw wire transfer identifier we used
    9459             :  * @param cb function to call on each transaction found
    9460             :  * @param cb_cls closure for @a cb
    9461             :  * @return query status of the transaction
    9462             :  */
    9463             : static enum GNUNET_DB_QueryStatus
    9464           0 : postgres_lookup_wire_transfer (
    9465             :   void *cls,
    9466             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    9467             :   TALER_EXCHANGEDB_AggregationDataCallback cb,
    9468             :   void *cb_cls)
    9469             : {
    9470           0 :   struct PostgresClosure *pg = cls;
    9471           0 :   struct GNUNET_PQ_QueryParam params[] = {
    9472           0 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    9473             :     GNUNET_PQ_query_param_end
    9474             :   };
    9475             :   struct WireTransferResultContext ctx;
    9476             :   enum GNUNET_DB_QueryStatus qs;
    9477             : 
    9478           0 :   ctx.cb = cb;
    9479           0 :   ctx.cb_cls = cb_cls;
    9480           0 :   ctx.pg = pg;
    9481           0 :   ctx.status = GNUNET_OK;
    9482             :   /* check if the melt record exists and get it */
    9483           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    9484             :                                              "lookup_transactions",
    9485             :                                              params,
    9486             :                                              &handle_wt_result,
    9487             :                                              &ctx);
    9488           0 :   if (GNUNET_OK != ctx.status)
    9489           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    9490           0 :   return qs;
    9491             : }
    9492             : 
    9493             : 
    9494             : /**
    9495             :  * Try to find the wire transfer details for a deposit operation.
    9496             :  * If we did not execute the deposit yet, return when it is supposed
    9497             :  * to be executed.
    9498             :  *
    9499             :  * @param cls closure
    9500             :  * @param h_contract_terms hash of the proposal data
    9501             :  * @param h_wire hash of merchant wire details
    9502             :  * @param coin_pub public key of deposited coin
    9503             :  * @param merchant_pub merchant public key
    9504             :  * @param[out] pending set to true if the transaction is still pending
    9505             :  * @param[out] wtid wire transfer identifier, only set if @a pending is false
    9506             :  * @param[out] exec_time when was the transaction done, or
    9507             :  *         when we expect it to be done (if @a pending is false)
    9508             :  * @param[out] amount_with_fee set to the total deposited amount
    9509             :  * @param[out] deposit_fee set to how much the exchange did charge for the deposit
    9510             :  * @param[out] kyc set to the kyc status of the receiver (if @a pending)
    9511             :  * @return transaction status code
    9512             :  */
    9513             : static enum GNUNET_DB_QueryStatus
    9514           0 : postgres_lookup_transfer_by_deposit (
    9515             :   void *cls,
    9516             :   const struct TALER_PrivateContractHashP *h_contract_terms,
    9517             :   const struct TALER_MerchantWireHashP *h_wire,
    9518             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    9519             :   const struct TALER_MerchantPublicKeyP *merchant_pub,
    9520             :   bool *pending,
    9521             :   struct TALER_WireTransferIdentifierRawP *wtid,
    9522             :   struct GNUNET_TIME_Timestamp *exec_time,
    9523             :   struct TALER_Amount *amount_with_fee,
    9524             :   struct TALER_Amount *deposit_fee,
    9525             :   struct TALER_EXCHANGEDB_KycStatus *kyc)
    9526             : {
    9527           0 :   struct PostgresClosure *pg = cls;
    9528             :   enum GNUNET_DB_QueryStatus qs;
    9529           0 :   struct GNUNET_PQ_QueryParam params[] = {
    9530           0 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    9531           0 :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    9532           0 :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    9533             :     GNUNET_PQ_query_param_end
    9534             :   };
    9535             :   char *payto_uri;
    9536             :   struct TALER_WireSaltP wire_salt;
    9537           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    9538           0 :     GNUNET_PQ_result_spec_auto_from_type ("wtid_raw",
    9539             :                                           wtid),
    9540           0 :     GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
    9541             :                                           &wire_salt),
    9542           0 :     GNUNET_PQ_result_spec_string ("payto_uri",
    9543             :                                   &payto_uri),
    9544           0 :     GNUNET_PQ_result_spec_timestamp ("execution_date",
    9545             :                                      exec_time),
    9546           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    9547             :                                  amount_with_fee),
    9548           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    9549             :                                  deposit_fee),
    9550             :     GNUNET_PQ_result_spec_end
    9551             :   };
    9552             : 
    9553           0 :   memset (kyc,
    9554             :           0,
    9555             :           sizeof (*kyc));
    9556             :   /* check if the aggregation record exists and get it */
    9557           0 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    9558             :                                                  "lookup_deposit_wtid",
    9559             :                                                  params,
    9560             :                                                  rs);
    9561           0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    9562             :   {
    9563             :     struct TALER_MerchantWireHashP wh;
    9564             : 
    9565           0 :     TALER_merchant_wire_signature_hash (payto_uri,
    9566             :                                         &wire_salt,
    9567             :                                         &wh);
    9568           0 :     GNUNET_PQ_cleanup_result (rs);
    9569           0 :     if (0 ==
    9570           0 :         GNUNET_memcmp (&wh,
    9571             :                        h_wire))
    9572             :     {
    9573           0 :       *pending = false;
    9574           0 :       kyc->ok = true;
    9575           0 :       return qs;
    9576             :     }
    9577           0 :     qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    9578             :   }
    9579           0 :   if (0 > qs)
    9580           0 :     return qs;
    9581           0 :   *pending = true;
    9582           0 :   memset (wtid,
    9583             :           0,
    9584             :           sizeof (*wtid));
    9585           0 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
    9586           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    9587             :               "lookup_deposit_wtid returned 0 matching rows\n");
    9588             :   {
    9589             :     /* Check if transaction exists in deposits, so that we just
    9590             :        do not have a WTID yet. In that case, return without wtid
    9591             :        (by setting 'pending' true). */
    9592           0 :     struct GNUNET_PQ_ResultSpec rs2[] = {
    9593           0 :       GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
    9594             :                                             &wire_salt),
    9595           0 :       GNUNET_PQ_result_spec_string ("payto_uri",
    9596             :                                     &payto_uri),
    9597           0 :       GNUNET_PQ_result_spec_allow_null (
    9598             :         GNUNET_PQ_result_spec_uint64 ("legitimization_requirement_serial_id",
    9599             :                                       &kyc->requirement_row),
    9600             :         NULL),
    9601           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    9602             :                                    amount_with_fee),
    9603           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    9604             :                                    deposit_fee),
    9605           0 :       GNUNET_PQ_result_spec_timestamp ("wire_deadline",
    9606             :                                        exec_time),
    9607             :       GNUNET_PQ_result_spec_end
    9608             :     };
    9609             : 
    9610           0 :     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    9611             :                                                    "get_deposit_without_wtid",
    9612             :                                                    params,
    9613             :                                                    rs2);
    9614           0 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    9615             :     {
    9616             :       struct TALER_MerchantWireHashP wh;
    9617             : 
    9618           0 :       if (0 == kyc->requirement_row)
    9619           0 :         kyc->ok = true; /* technically: unknown */
    9620           0 :       TALER_merchant_wire_signature_hash (payto_uri,
    9621             :                                           &wire_salt,
    9622             :                                           &wh);
    9623           0 :       GNUNET_PQ_cleanup_result (rs);
    9624           0 :       if (0 !=
    9625           0 :           GNUNET_memcmp (&wh,
    9626             :                          h_wire))
    9627           0 :         return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    9628             :     }
    9629           0 :     return qs;
    9630             :   }
    9631             : }
    9632             : 
    9633             : 
    9634             : /**
    9635             :  * Function called to insert aggregation information into the DB.
    9636             :  *
    9637             :  * @param cls closure
    9638             :  * @param wtid the raw wire transfer identifier we used
    9639             :  * @param deposit_serial_id row in the deposits table for which this is aggregation data
    9640             :  * @return transaction status code
    9641             :  */
    9642             : static enum GNUNET_DB_QueryStatus
    9643           0 : postgres_insert_aggregation_tracking (
    9644             :   void *cls,
    9645             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    9646             :   unsigned long long deposit_serial_id)
    9647             : {
    9648           0 :   struct PostgresClosure *pg = cls;
    9649           0 :   uint64_t rid = deposit_serial_id;
    9650           0 :   struct GNUNET_PQ_QueryParam params[] = {
    9651           0 :     GNUNET_PQ_query_param_uint64 (&rid),
    9652           0 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    9653             :     GNUNET_PQ_query_param_end
    9654             :   };
    9655             : 
    9656           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    9657             :                                              "insert_aggregation_tracking",
    9658             :                                              params);
    9659             : }
    9660             : 
    9661             : 
    9662             : /**
    9663             :  * Obtain wire fee from database.
    9664             :  *
    9665             :  * @param cls closure
    9666             :  * @param type type of wire transfer the fee applies for
    9667             :  * @param date for which date do we want the fee?
    9668             :  * @param[out] start_date when does the fee go into effect
    9669             :  * @param[out] end_date when does the fee end being valid
    9670             :  * @param[out] fees how high are the wire fees
    9671             :  * @param[out] master_sig signature over the above by the exchange master key
    9672             :  * @return status of the transaction
    9673             :  */
    9674             : static enum GNUNET_DB_QueryStatus
    9675           0 : postgres_get_wire_fee (void *cls,
    9676             :                        const char *type,
    9677             :                        struct GNUNET_TIME_Timestamp date,
    9678             :                        struct GNUNET_TIME_Timestamp *start_date,
    9679             :                        struct GNUNET_TIME_Timestamp *end_date,
    9680             :                        struct TALER_WireFeeSet *fees,
    9681             :                        struct TALER_MasterSignatureP *master_sig)
    9682             : {
    9683           0 :   struct PostgresClosure *pg = cls;
    9684           0 :   struct GNUNET_PQ_QueryParam params[] = {
    9685           0 :     GNUNET_PQ_query_param_string (type),
    9686           0 :     GNUNET_PQ_query_param_timestamp (&date),
    9687             :     GNUNET_PQ_query_param_end
    9688             :   };
    9689           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    9690           0 :     GNUNET_PQ_result_spec_timestamp ("start_date",
    9691             :                                      start_date),
    9692           0 :     GNUNET_PQ_result_spec_timestamp ("end_date",
    9693             :                                      end_date),
    9694           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
    9695             :                                  &fees->wire),
    9696           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("wad_fee",
    9697             :                                  &fees->wad),
    9698           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
    9699             :                                  &fees->closing),
    9700           0 :     GNUNET_PQ_result_spec_auto_from_type ("master_sig",
    9701             :                                           master_sig),
    9702             :     GNUNET_PQ_result_spec_end
    9703             :   };
    9704             : 
    9705           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    9706             :                                                    "get_wire_fee",
    9707             :                                                    params,
    9708             :                                                    rs);
    9709             : }
    9710             : 
    9711             : 
    9712             : /**
    9713             :  * Obtain global fees from database.
    9714             :  *
    9715             :  * @param cls closure
    9716             :  * @param date for which date do we want the fee?
    9717             :  * @param[out] start_date when does the fee go into effect
    9718             :  * @param[out] end_date when does the fee end being valid
    9719             :  * @param[out] fees how high are the wire fees
    9720             :  * @param[out] purse_timeout set to how long we keep unmerged purses
    9721             :  * @param[out] kyc_timeout set to how long we keep accounts without KYC
    9722             :  * @param[out] history_expiration set to how long we keep account histories
    9723             :  * @param[out] purse_account_limit set to the number of free purses per account
    9724             :  * @param[out] master_sig signature over the above by the exchange master key
    9725             :  * @return status of the transaction
    9726             :  */
    9727             : static enum GNUNET_DB_QueryStatus
    9728           0 : postgres_get_global_fee (void *cls,
    9729             :                          struct GNUNET_TIME_Timestamp date,
    9730             :                          struct GNUNET_TIME_Timestamp *start_date,
    9731             :                          struct GNUNET_TIME_Timestamp *end_date,
    9732             :                          struct TALER_GlobalFeeSet *fees,
    9733             :                          struct GNUNET_TIME_Relative *purse_timeout,
    9734             :                          struct GNUNET_TIME_Relative *kyc_timeout,
    9735             :                          struct GNUNET_TIME_Relative *history_expiration,
    9736             :                          uint32_t *purse_account_limit,
    9737             :                          struct TALER_MasterSignatureP *master_sig)
    9738             : {
    9739           0 :   struct PostgresClosure *pg = cls;
    9740           0 :   struct GNUNET_PQ_QueryParam params[] = {
    9741           0 :     GNUNET_PQ_query_param_timestamp (&date),
    9742             :     GNUNET_PQ_query_param_end
    9743             :   };
    9744           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    9745           0 :     GNUNET_PQ_result_spec_timestamp ("start_date",
    9746             :                                      start_date),
    9747           0 :     GNUNET_PQ_result_spec_timestamp ("end_date",
    9748             :                                      end_date),
    9749           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
    9750             :                                  &fees->history),
    9751           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("kyc_fee",
    9752             :                                  &fees->kyc),
    9753           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("account_fee",
    9754             :                                  &fees->account),
    9755           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee",
    9756             :                                  &fees->purse),
    9757           0 :     GNUNET_PQ_result_spec_relative_time ("purse_timeout",
    9758             :                                          purse_timeout),
    9759           0 :     GNUNET_PQ_result_spec_relative_time ("kyc_timeout",
    9760             :                                          kyc_timeout),
    9761           0 :     GNUNET_PQ_result_spec_relative_time ("history_expiration",
    9762             :                                          history_expiration),
    9763           0 :     GNUNET_PQ_result_spec_uint32 ("purse_account_limit",
    9764             :                                   purse_account_limit),
    9765           0 :     GNUNET_PQ_result_spec_auto_from_type ("master_sig",
    9766             :                                           master_sig),
    9767             :     GNUNET_PQ_result_spec_end
    9768             :   };
    9769             : 
    9770           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    9771             :                                                    "get_global_fee",
    9772             :                                                    params,
    9773             :                                                    rs);
    9774             : }
    9775             : 
    9776             : 
    9777             : /**
    9778             :  * Closure for #global_fees_cb().
    9779             :  */
    9780             : struct GlobalFeeContext
    9781             : {
    9782             :   /**
    9783             :    * Function to call for each global fee block.
    9784             :    */
    9785             :   TALER_EXCHANGEDB_GlobalFeeCallback cb;
    9786             : 
    9787             :   /**
    9788             :    * Closure to give to @e rec.
    9789             :    */
    9790             :   void *cb_cls;
    9791             : 
    9792             :   /**
    9793             :    * Plugin context.
    9794             :    */
    9795             :   struct PostgresClosure *pg;
    9796             : 
    9797             :   /**
    9798             :    * Set to #GNUNET_SYSERR on error.
    9799             :    */
    9800             :   enum GNUNET_GenericReturnValue status;
    9801             : };
    9802             : 
    9803             : 
    9804             : /**
    9805             :  * Function to be called with the results of a SELECT statement
    9806             :  * that has returned @a num_results results.
    9807             :  *
    9808             :  * @param cls closure
    9809             :  * @param result the postgres result
    9810             :  * @param num_results the number of results in @a result
    9811             :  */
    9812             : static void
    9813           0 : global_fees_cb (void *cls,
    9814             :                 PGresult *result,
    9815             :                 unsigned int num_results)
    9816             : {
    9817           0 :   struct GlobalFeeContext *gctx = cls;
    9818           0 :   struct PostgresClosure *pg = gctx->pg;
    9819             : 
    9820           0 :   for (unsigned int i = 0; i<num_results; i++)
    9821             :   {
    9822             :     struct TALER_GlobalFeeSet fees;
    9823             :     struct GNUNET_TIME_Relative purse_timeout;
    9824             :     struct GNUNET_TIME_Relative kyc_timeout;
    9825             :     struct GNUNET_TIME_Relative history_expiration;
    9826             :     uint32_t purse_account_limit;
    9827             :     struct GNUNET_TIME_Timestamp start_date;
    9828             :     struct GNUNET_TIME_Timestamp end_date;
    9829             :     struct TALER_MasterSignatureP master_sig;
    9830           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
    9831           0 :       GNUNET_PQ_result_spec_timestamp ("start_date",
    9832             :                                        &start_date),
    9833           0 :       GNUNET_PQ_result_spec_timestamp ("end_date",
    9834             :                                        &end_date),
    9835           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
    9836             :                                    &fees.history),
    9837           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("kyc_fee",
    9838             :                                    &fees.kyc),
    9839           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("account_fee",
    9840             :                                    &fees.account),
    9841           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee",
    9842             :                                    &fees.purse),
    9843           0 :       GNUNET_PQ_result_spec_relative_time ("purse_timeout",
    9844             :                                            &purse_timeout),
    9845           0 :       GNUNET_PQ_result_spec_relative_time ("kyc_timeout",
    9846             :                                            &kyc_timeout),
    9847           0 :       GNUNET_PQ_result_spec_relative_time ("history_expiration",
    9848             :                                            &history_expiration),
    9849           0 :       GNUNET_PQ_result_spec_uint32 ("purse_account_limit",
    9850             :                                     &purse_account_limit),
    9851           0 :       GNUNET_PQ_result_spec_auto_from_type ("master_sig",
    9852             :                                             &master_sig),
    9853             :       GNUNET_PQ_result_spec_end
    9854             :     };
    9855           0 :     if (GNUNET_OK !=
    9856           0 :         GNUNET_PQ_extract_result (result,
    9857             :                                   rs,
    9858             :                                   i))
    9859             :     {
    9860           0 :       GNUNET_break (0);
    9861           0 :       gctx->status = GNUNET_SYSERR;
    9862           0 :       break;
    9863             :     }
    9864           0 :     gctx->cb (gctx->cb_cls,
    9865             :               &fees,
    9866             :               purse_timeout,
    9867             :               kyc_timeout,
    9868             :               history_expiration,
    9869             :               purse_account_limit,
    9870             :               start_date,
    9871             :               end_date,
    9872             :               &master_sig);
    9873           0 :     GNUNET_PQ_cleanup_result (rs);
    9874             :   }
    9875           0 : }
    9876             : 
    9877             : 
    9878             : /**
    9879             :  * Obtain global fees from database.
    9880             :  *
    9881             :  * @param cls closure
    9882             :  * @param cb function to call on each fee entry
    9883             :  * @param cb_cls closure for @a cb
    9884             :  * @return status of the transaction
    9885             :  */
    9886             : static enum GNUNET_DB_QueryStatus
    9887           0 : postgres_get_global_fees (void *cls,
    9888             :                           TALER_EXCHANGEDB_GlobalFeeCallback cb,
    9889             :                           void *cb_cls)
    9890             : {
    9891           0 :   struct PostgresClosure *pg = cls;
    9892             :   struct GNUNET_TIME_Timestamp date
    9893           0 :     = GNUNET_TIME_absolute_to_timestamp (
    9894             :         GNUNET_TIME_absolute_subtract (
    9895             :           GNUNET_TIME_absolute_get (),
    9896             :           GNUNET_TIME_UNIT_YEARS));
    9897           0 :   struct GNUNET_PQ_QueryParam params[] = {
    9898           0 :     GNUNET_PQ_query_param_timestamp (&date),
    9899             :     GNUNET_PQ_query_param_end
    9900             :   };
    9901           0 :   struct GlobalFeeContext gctx = {
    9902             :     .cb = cb,
    9903             :     .cb_cls = cb_cls,
    9904             :     .pg = pg,
    9905             :     .status = GNUNET_OK
    9906             :   };
    9907             : 
    9908           0 :   return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    9909             :                                                "get_global_fees",
    9910             :                                                params,
    9911             :                                                &global_fees_cb,
    9912             :                                                &gctx);
    9913             : }
    9914             : 
    9915             : 
    9916             : /**
    9917             :  * Insert wire transfer fee into database.
    9918             :  *
    9919             :  * @param cls closure
    9920             :  * @param type type of wire transfer this fee applies for
    9921             :  * @param start_date when does the fee go into effect
    9922             :  * @param end_date when does the fee end being valid
    9923             :  * @param fees how high are the wire fees
    9924             :  * @param master_sig signature over the above by the exchange master key
    9925             :  * @return transaction status code
    9926             :  */
    9927             : static enum GNUNET_DB_QueryStatus
    9928           0 : postgres_insert_wire_fee (void *cls,
    9929             :                           const char *type,
    9930             :                           struct GNUNET_TIME_Timestamp start_date,
    9931             :                           struct GNUNET_TIME_Timestamp end_date,
    9932             :                           const struct TALER_WireFeeSet *fees,
    9933             :                           const struct TALER_MasterSignatureP *master_sig)
    9934             : {
    9935           0 :   struct PostgresClosure *pg = cls;
    9936           0 :   struct GNUNET_PQ_QueryParam params[] = {
    9937           0 :     GNUNET_PQ_query_param_string (type),
    9938           0 :     GNUNET_PQ_query_param_timestamp (&start_date),
    9939           0 :     GNUNET_PQ_query_param_timestamp (&end_date),
    9940           0 :     TALER_PQ_query_param_amount (&fees->wire),
    9941           0 :     TALER_PQ_query_param_amount (&fees->closing),
    9942           0 :     TALER_PQ_query_param_amount (&fees->wad),
    9943           0 :     GNUNET_PQ_query_param_auto_from_type (master_sig),
    9944             :     GNUNET_PQ_query_param_end
    9945             :   };
    9946             :   struct TALER_WireFeeSet wx;
    9947             :   struct TALER_MasterSignatureP sig;
    9948             :   struct GNUNET_TIME_Timestamp sd;
    9949             :   struct GNUNET_TIME_Timestamp ed;
    9950             :   enum GNUNET_DB_QueryStatus qs;
    9951             : 
    9952           0 :   qs = postgres_get_wire_fee (pg,
    9953             :                               type,
    9954             :                               start_date,
    9955             :                               &sd,
    9956             :                               &ed,
    9957             :                               &wx,
    9958             :                               &sig);
    9959           0 :   if (qs < 0)
    9960           0 :     return qs;
    9961           0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    9962             :   {
    9963           0 :     if (0 != GNUNET_memcmp (&sig,
    9964             :                             master_sig))
    9965             :     {
    9966           0 :       GNUNET_break (0);
    9967           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    9968             :     }
    9969           0 :     if (0 !=
    9970           0 :         TALER_wire_fee_set_cmp (fees,
    9971             :                                 &wx))
    9972             :     {
    9973           0 :       GNUNET_break (0);
    9974           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    9975             :     }
    9976           0 :     if ( (GNUNET_TIME_timestamp_cmp (sd,
    9977             :                                      !=,
    9978           0 :                                      start_date)) ||
    9979           0 :          (GNUNET_TIME_timestamp_cmp (ed,
    9980             :                                      !=,
    9981             :                                      end_date)) )
    9982             :     {
    9983           0 :       GNUNET_break (0);
    9984           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    9985             :     }
    9986             :     /* equal record already exists */
    9987           0 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    9988             :   }
    9989             : 
    9990           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    9991             :                                              "insert_wire_fee",
    9992             :                                              params);
    9993             : }
    9994             : 
    9995             : 
    9996             : /**
    9997             :  * Insert global fee data into database.
    9998             :  *
    9999             :  * @param cls closure
   10000             :  * @param start_date when does the fees go into effect
   10001             :  * @param end_date when does the fees end being valid
   10002             :  * @param fees how high is are the global fees
   10003             :  * @param purse_timeout when do purses time out
   10004             :  * @param kyc_timeout when do reserves without KYC time out
   10005             :  * @param history_expiration how long are account histories preserved
   10006             :  * @param purse_account_limit how many purses are free per account
   10007             :  * @param master_sig signature over the above by the exchange master key
   10008             :  * @return transaction status code
   10009             :  */
   10010             : static enum GNUNET_DB_QueryStatus
   10011           0 : postgres_insert_global_fee (void *cls,
   10012             :                             struct GNUNET_TIME_Timestamp start_date,
   10013             :                             struct GNUNET_TIME_Timestamp end_date,
   10014             :                             const struct TALER_GlobalFeeSet *fees,
   10015             :                             struct GNUNET_TIME_Relative purse_timeout,
   10016             :                             struct GNUNET_TIME_Relative kyc_timeout,
   10017             :                             struct GNUNET_TIME_Relative history_expiration,
   10018             :                             uint32_t purse_account_limit,
   10019             :                             const struct TALER_MasterSignatureP *master_sig)
   10020             : {
   10021           0 :   struct PostgresClosure *pg = cls;
   10022           0 :   struct GNUNET_PQ_QueryParam params[] = {
   10023           0 :     GNUNET_PQ_query_param_timestamp (&start_date),
   10024           0 :     GNUNET_PQ_query_param_timestamp (&end_date),
   10025           0 :     TALER_PQ_query_param_amount (&fees->history),
   10026           0 :     TALER_PQ_query_param_amount (&fees->kyc),
   10027           0 :     TALER_PQ_query_param_amount (&fees->account),
   10028           0 :     TALER_PQ_query_param_amount (&fees->purse),
   10029           0 :     GNUNET_PQ_query_param_relative_time (&purse_timeout),
   10030           0 :     GNUNET_PQ_query_param_relative_time (&kyc_timeout),
   10031           0 :     GNUNET_PQ_query_param_relative_time (&history_expiration),
   10032           0 :     GNUNET_PQ_query_param_uint32 (&purse_account_limit),
   10033           0 :     GNUNET_PQ_query_param_auto_from_type (master_sig),
   10034             :     GNUNET_PQ_query_param_end
   10035             :   };
   10036             :   struct TALER_GlobalFeeSet wx;
   10037             :   struct TALER_MasterSignatureP sig;
   10038             :   struct GNUNET_TIME_Timestamp sd;
   10039             :   struct GNUNET_TIME_Timestamp ed;
   10040             :   enum GNUNET_DB_QueryStatus qs;
   10041             :   struct GNUNET_TIME_Relative pt;
   10042             :   struct GNUNET_TIME_Relative kt;
   10043             :   struct GNUNET_TIME_Relative he;
   10044             :   uint32_t pal;
   10045             : 
   10046           0 :   qs = postgres_get_global_fee (pg,
   10047             :                                 start_date,
   10048             :                                 &sd,
   10049             :                                 &ed,
   10050             :                                 &wx,
   10051             :                                 &pt,
   10052             :                                 &kt,
   10053             :                                 &he,
   10054             :                                 &pal,
   10055             :                                 &sig);
   10056           0 :   if (qs < 0)
   10057           0 :     return qs;
   10058           0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
   10059             :   {
   10060           0 :     if (0 != GNUNET_memcmp (&sig,
   10061             :                             master_sig))
   10062             :     {
   10063           0 :       GNUNET_break (0);
   10064           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
   10065             :     }
   10066           0 :     if (0 !=
   10067           0 :         TALER_global_fee_set_cmp (fees,
   10068             :                                   &wx))
   10069             :     {
   10070           0 :       GNUNET_break (0);
   10071           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
   10072             :     }
   10073           0 :     if ( (GNUNET_TIME_timestamp_cmp (sd,
   10074             :                                      !=,
   10075           0 :                                      start_date)) ||
   10076           0 :          (GNUNET_TIME_timestamp_cmp (ed,
   10077             :                                      !=,
   10078             :                                      end_date)) )
   10079             :     {
   10080           0 :       GNUNET_break (0);
   10081           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
   10082             :     }
   10083           0 :     if ( (GNUNET_TIME_relative_cmp (purse_timeout,
   10084             :                                     !=,
   10085           0 :                                     pt)) ||
   10086           0 :          (GNUNET_TIME_relative_cmp (kyc_timeout,
   10087             :                                     !=,
   10088           0 :                                     kt)) ||
   10089           0 :          (GNUNET_TIME_relative_cmp (history_expiration,
   10090             :                                     !=,
   10091             :                                     he)) )
   10092             :     {
   10093           0 :       GNUNET_break (0);
   10094           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
   10095             :     }
   10096           0 :     if (purse_account_limit != pal)
   10097             :     {
   10098           0 :       GNUNET_break (0);
   10099           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
   10100             :     }
   10101             :     /* equal record already exists */
   10102           0 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
   10103             :   }
   10104             : 
   10105           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   10106             :                                              "insert_global_fee",
   10107             :                                              params);
   10108             : }
   10109             : 
   10110             : 
   10111             : /**
   10112             :  * Closure for #reserve_expired_cb().
   10113             :  */
   10114             : struct ExpiredReserveContext
   10115             : {
   10116             :   /**
   10117             :    * Function to call for each expired reserve.
   10118             :    */
   10119             :   TALER_EXCHANGEDB_ReserveExpiredCallback rec;
   10120             : 
   10121             :   /**
   10122             :    * Closure to give to @e rec.
   10123             :    */
   10124             :   void *rec_cls;
   10125             : 
   10126             :   /**
   10127             :    * Plugin context.
   10128             :    */
   10129             :   struct PostgresClosure *pg;
   10130             : 
   10131             :   /**
   10132             :    * Set to #GNUNET_SYSERR on error.
   10133             :    */
   10134             :   enum GNUNET_GenericReturnValue status;
   10135             : };
   10136             : 
   10137             : 
   10138             : /**
   10139             :  * Function to be called with the results of a SELECT statement
   10140             :  * that has returned @a num_results results.
   10141             :  *
   10142             :  * @param cls closure
   10143             :  * @param result the postgres result
   10144             :  * @param num_results the number of results in @a result
   10145             :  */
   10146             : static void
   10147           0 : reserve_expired_cb (void *cls,
   10148             :                     PGresult *result,
   10149             :                     unsigned int num_results)
   10150             : {
   10151           0 :   struct ExpiredReserveContext *erc = cls;
   10152           0 :   struct PostgresClosure *pg = erc->pg;
   10153             :   enum GNUNET_GenericReturnValue ret;
   10154             : 
   10155           0 :   ret = GNUNET_OK;
   10156           0 :   for (unsigned int i = 0; i<num_results; i++)
   10157             :   {
   10158             :     struct GNUNET_TIME_Timestamp exp_date;
   10159             :     char *account_details;
   10160             :     struct TALER_ReservePublicKeyP reserve_pub;
   10161             :     struct TALER_Amount remaining_balance;
   10162           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   10163           0 :       GNUNET_PQ_result_spec_timestamp ("expiration_date",
   10164             :                                        &exp_date),
   10165           0 :       GNUNET_PQ_result_spec_string ("account_details",
   10166             :                                     &account_details),
   10167           0 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
   10168             :                                             &reserve_pub),
   10169           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
   10170             :                                    &remaining_balance),
   10171             :       GNUNET_PQ_result_spec_end
   10172             :     };
   10173             : 
   10174           0 :     if (GNUNET_OK !=
   10175           0 :         GNUNET_PQ_extract_result (result,
   10176             :                                   rs,
   10177             :                                   i))
   10178             :     {
   10179           0 :       GNUNET_break (0);
   10180           0 :       ret = GNUNET_SYSERR;
   10181           0 :       break;
   10182             :     }
   10183           0 :     ret = erc->rec (erc->rec_cls,
   10184             :                     &reserve_pub,
   10185             :                     &remaining_balance,
   10186             :                     account_details,
   10187             :                     exp_date);
   10188           0 :     GNUNET_PQ_cleanup_result (rs);
   10189           0 :     if (GNUNET_OK != ret)
   10190           0 :       break;
   10191             :   }
   10192           0 :   erc->status = ret;
   10193           0 : }
   10194             : 
   10195             : 
   10196             : /**
   10197             :  * Obtain information about expired reserves and their
   10198             :  * remaining balances.
   10199             :  *
   10200             :  * @param cls closure of the plugin
   10201             :  * @param now timestamp based on which we decide expiration
   10202             :  * @param rec function to call on expired reserves
   10203             :  * @param rec_cls closure for @a rec
   10204             :  * @return transaction status
   10205             :  */
   10206             : static enum GNUNET_DB_QueryStatus
   10207           0 : postgres_get_expired_reserves (void *cls,
   10208             :                                struct GNUNET_TIME_Timestamp now,
   10209             :                                TALER_EXCHANGEDB_ReserveExpiredCallback rec,
   10210             :                                void *rec_cls)
   10211             : {
   10212           0 :   struct PostgresClosure *pg = cls;
   10213           0 :   struct GNUNET_PQ_QueryParam params[] = {
   10214           0 :     GNUNET_PQ_query_param_timestamp (&now),
   10215             :     GNUNET_PQ_query_param_end
   10216             :   };
   10217           0 :   struct ExpiredReserveContext ectx = {
   10218             :     .rec = rec,
   10219             :     .rec_cls = rec_cls,
   10220             :     .pg = pg,
   10221             :     .status = GNUNET_OK
   10222             :   };
   10223             :   enum GNUNET_DB_QueryStatus qs;
   10224             : 
   10225           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   10226             :                                              "get_expired_reserves",
   10227             :                                              params,
   10228             :                                              &reserve_expired_cb,
   10229             :                                              &ectx);
   10230           0 :   switch (ectx.status)
   10231             :   {
   10232           0 :   case GNUNET_SYSERR:
   10233           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   10234           0 :   case GNUNET_NO:
   10235           0 :     return GNUNET_DB_STATUS_SOFT_ERROR;
   10236           0 :   case GNUNET_OK:
   10237           0 :     break;
   10238             :   }
   10239           0 :   return qs;
   10240             : }
   10241             : 
   10242             : 
   10243             : /**
   10244             :  * Insert reserve close operation into database.
   10245             :  *
   10246             :  * @param cls closure
   10247             :  * @param reserve_pub which reserve is this about?
   10248             :  * @param execution_date when did we perform the transfer?
   10249             :  * @param receiver_account to which account do we transfer?
   10250             :  * @param wtid wire transfer details
   10251             :  * @param amount_with_fee amount we charged to the reserve
   10252             :  * @param closing_fee how high is the closing fee
   10253             :  * @return transaction status code
   10254             :  */
   10255             : static enum GNUNET_DB_QueryStatus
   10256           0 : postgres_insert_reserve_closed (
   10257             :   void *cls,
   10258             :   const struct TALER_ReservePublicKeyP *reserve_pub,
   10259             :   struct GNUNET_TIME_Timestamp execution_date,
   10260             :   const char *receiver_account,
   10261             :   const struct TALER_WireTransferIdentifierRawP *wtid,
   10262             :   const struct TALER_Amount *amount_with_fee,
   10263             :   const struct TALER_Amount *closing_fee)
   10264             : {
   10265           0 :   struct PostgresClosure *pg = cls;
   10266             :   struct TALER_EXCHANGEDB_Reserve reserve;
   10267             :   enum GNUNET_DB_QueryStatus qs;
   10268             :   struct TALER_PaytoHashP h_payto;
   10269             : 
   10270           0 :   TALER_payto_hash (receiver_account,
   10271             :                     &h_payto);
   10272             :   {
   10273           0 :     struct GNUNET_PQ_QueryParam params[] = {
   10274           0 :       GNUNET_PQ_query_param_auto_from_type (reserve_pub),
   10275           0 :       GNUNET_PQ_query_param_timestamp (&execution_date),
   10276           0 :       GNUNET_PQ_query_param_auto_from_type (wtid),
   10277           0 :       GNUNET_PQ_query_param_auto_from_type (&h_payto),
   10278           0 :       TALER_PQ_query_param_amount (amount_with_fee),
   10279           0 :       TALER_PQ_query_param_amount (closing_fee),
   10280             :       GNUNET_PQ_query_param_end
   10281             :     };
   10282             : 
   10283           0 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
   10284             :                                              "reserves_close_insert",
   10285             :                                              params);
   10286             :   }
   10287           0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   10288           0 :     return qs;
   10289             : 
   10290             :   /* update reserve balance */
   10291           0 :   reserve.pub = *reserve_pub;
   10292           0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   10293           0 :       (qs = postgres_reserves_get (cls,
   10294             :                                    &reserve)))
   10295             :   {
   10296             :     /* Existence should have been checked before we got here... */
   10297           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   10298           0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   10299           0 :       qs = GNUNET_DB_STATUS_HARD_ERROR;
   10300           0 :     return qs;
   10301             :   }
   10302             :   {
   10303             :     enum TALER_AmountArithmeticResult ret;
   10304             : 
   10305           0 :     ret = TALER_amount_subtract (&reserve.balance,
   10306             :                                  &reserve.balance,
   10307             :                                  amount_with_fee);
   10308           0 :     if (ret < 0)
   10309             :     {
   10310             :       /* The reserve history was checked to make sure there is enough of a balance
   10311             :          left before we tried this; however, concurrent operations may have changed
   10312             :          the situation by now.  We should re-try the transaction.  */
   10313           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   10314             :                   "Closing of reserve `%s' refused due to balance mismatch. Retrying.\n",
   10315             :                   TALER_B2S (reserve_pub));
   10316           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
   10317             :     }
   10318           0 :     GNUNET_break (TALER_AAR_RESULT_ZERO == ret);
   10319             :   }
   10320           0 :   return reserves_update (cls,
   10321             :                           &reserve);
   10322             : }
   10323             : 
   10324             : 
   10325             : /**
   10326             :  * Function called to insert wire transfer commit data into the DB.
   10327             :  *
   10328             :  * @param cls closure
   10329             :  * @param type type of the wire transfer (i.e. "iban")
   10330             :  * @param buf buffer with wire transfer preparation data
   10331             :  * @param buf_size number of bytes in @a buf
   10332             :  * @return query status code
   10333             :  */
   10334             : static enum GNUNET_DB_QueryStatus
   10335           0 : postgres_wire_prepare_data_insert (void *cls,
   10336             :                                    const char *type,
   10337             :                                    const char *buf,
   10338             :                                    size_t buf_size)
   10339             : {
   10340           0 :   struct PostgresClosure *pg = cls;
   10341           0 :   struct GNUNET_PQ_QueryParam params[] = {
   10342           0 :     GNUNET_PQ_query_param_string (type),
   10343           0 :     GNUNET_PQ_query_param_fixed_size (buf, buf_size),
   10344             :     GNUNET_PQ_query_param_end
   10345             :   };
   10346             : 
   10347           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   10348             :                                              "wire_prepare_data_insert",
   10349             :                                              params);
   10350             : }
   10351             : 
   10352             : 
   10353             : /**
   10354             :  * Function called to mark wire transfer commit data as finished.
   10355             :  *
   10356             :  * @param cls closure
   10357             :  * @param rowid which entry to mark as finished
   10358             :  * @return transaction status code
   10359             :  */
   10360             : static enum GNUNET_DB_QueryStatus
   10361           0 : postgres_wire_prepare_data_mark_finished (
   10362             :   void *cls,
   10363             :   uint64_t rowid)
   10364             : {
   10365           0 :   struct PostgresClosure *pg = cls;
   10366           0 :   struct GNUNET_PQ_QueryParam params[] = {
   10367           0 :     GNUNET_PQ_query_param_uint64 (&rowid),
   10368             :     GNUNET_PQ_query_param_end
   10369             :   };
   10370             : 
   10371           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   10372             :                                              "wire_prepare_data_mark_done",
   10373             :                                              params);
   10374             : }
   10375             : 
   10376             : 
   10377             : /**
   10378             :  * Function called to mark wire transfer commit data as failed.
   10379             :  *
   10380             :  * @param cls closure
   10381             :  * @param rowid which entry to mark as failed
   10382             :  * @return transaction status code
   10383             :  */
   10384             : static enum GNUNET_DB_QueryStatus
   10385           0 : postgres_wire_prepare_data_mark_failed (
   10386             :   void *cls,
   10387             :   uint64_t rowid)
   10388             : {
   10389           0 :   struct PostgresClosure *pg = cls;
   10390           0 :   struct GNUNET_PQ_QueryParam params[] = {
   10391           0 :     GNUNET_PQ_query_param_uint64 (&rowid),
   10392             :     GNUNET_PQ_query_param_end
   10393             :   };
   10394             : 
   10395           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   10396             :                                              "wire_prepare_data_mark_failed",
   10397             :                                              params);
   10398             : }
   10399             : 
   10400             : 
   10401             : /**
   10402             :  * Closure for #prewire_cb().
   10403             :  */
   10404             : struct PrewireContext
   10405             : {
   10406             :   /**
   10407             :    * Function to call on each result.
   10408             :    */
   10409             :   TALER_EXCHANGEDB_WirePreparationIterator cb;
   10410             : 
   10411             :   /**
   10412             :    * Closure for @a cb.
   10413             :    */
   10414             :   void *cb_cls;
   10415             : 
   10416             :   /**
   10417             :    * #GNUNET_OK if everything went fine.
   10418             :    */
   10419             :   enum GNUNET_GenericReturnValue status;
   10420             : };
   10421             : 
   10422             : 
   10423             : /**
   10424             :  * Invoke the callback for each result.
   10425             :  *
   10426             :  * @param cls a `struct MissingWireContext *`
   10427             :  * @param result SQL result
   10428             :  * @param num_results number of rows in @a result
   10429             :  */
   10430             : static void
   10431           0 : prewire_cb (void *cls,
   10432             :             PGresult *result,
   10433             :             unsigned int num_results)
   10434             : {
   10435           0 :   struct PrewireContext *pc = cls;
   10436             : 
   10437           0 :   for (unsigned int i = 0; i < num_results; i++)
   10438             :   {
   10439             :     uint64_t prewire_uuid;
   10440             :     char *wire_method;
   10441           0 :     void *buf = NULL;
   10442             :     size_t buf_size;
   10443           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   10444           0 :       GNUNET_PQ_result_spec_uint64 ("prewire_uuid",
   10445             :                                     &prewire_uuid),
   10446           0 :       GNUNET_PQ_result_spec_string ("wire_method",
   10447             :                                     &wire_method),
   10448           0 :       GNUNET_PQ_result_spec_variable_size ("buf",
   10449             :                                            &buf,
   10450             :                                            &buf_size),
   10451             :       GNUNET_PQ_result_spec_end
   10452             :     };
   10453             : 
   10454           0 :     if (GNUNET_OK !=
   10455           0 :         GNUNET_PQ_extract_result (result,
   10456             :                                   rs,
   10457             :                                   i))
   10458             :     {
   10459           0 :       GNUNET_break (0);
   10460           0 :       pc->status = GNUNET_SYSERR;
   10461           0 :       return;
   10462             :     }
   10463           0 :     pc->cb (pc->cb_cls,
   10464             :             prewire_uuid,
   10465             :             wire_method,
   10466             :             buf,
   10467             :             buf_size);
   10468           0 :     GNUNET_PQ_cleanup_result (rs);
   10469             :   }
   10470             : }
   10471             : 
   10472             : 
   10473             : /**
   10474             :  * Function called to get an unfinished wire transfer
   10475             :  * preparation data. Fetches at most one item.
   10476             :  *
   10477             :  * @param cls closure
   10478             :  * @param start_row offset to query table at
   10479             :  * @param limit maximum number of results to return
   10480             :  * @param cb function to call for ONE unfinished item
   10481             :  * @param cb_cls closure for @a cb
   10482             :  * @return transaction status code
   10483             :  */
   10484             : static enum GNUNET_DB_QueryStatus
   10485           0 : postgres_wire_prepare_data_get (void *cls,
   10486             :                                 uint64_t start_row,
   10487             :                                 uint64_t limit,
   10488             :                                 TALER_EXCHANGEDB_WirePreparationIterator cb,
   10489             :                                 void *cb_cls)
   10490             : {
   10491           0 :   struct PostgresClosure *pg = cls;
   10492           0 :   struct GNUNET_PQ_QueryParam params[] = {
   10493           0 :     GNUNET_PQ_query_param_uint64 (&start_row),
   10494           0 :     GNUNET_PQ_query_param_uint64 (&limit),
   10495             :     GNUNET_PQ_query_param_end
   10496             :   };
   10497           0 :   struct PrewireContext pc = {
   10498             :     .cb = cb,
   10499             :     .cb_cls = cb_cls,
   10500             :     .status = GNUNET_OK
   10501             :   };
   10502             :   enum GNUNET_DB_QueryStatus qs;
   10503             : 
   10504           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   10505             :                                              "wire_prepare_data_get",
   10506             :                                              params,
   10507             :                                              &prewire_cb,
   10508             :                                              &pc);
   10509           0 :   if (GNUNET_OK != pc.status)
   10510           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   10511           0 :   return qs;
   10512             : }
   10513             : 
   10514             : 
   10515             : /**
   10516             :  * Starts a READ COMMITTED transaction where we transiently violate the foreign
   10517             :  * constraints on the "wire_out" table as we insert aggregations
   10518             :  * and only add the wire transfer out at the end.
   10519             :  *
   10520             :  * @param cls the @e cls of this struct with the plugin-specific state
   10521             :  * @return #GNUNET_OK on success
   10522             :  */
   10523             : static enum GNUNET_GenericReturnValue
   10524           0 : postgres_start_deferred_wire_out (void *cls)
   10525             : {
   10526           0 :   struct PostgresClosure *pg = cls;
   10527           0 :   struct GNUNET_PQ_ExecuteStatement es[] = {
   10528           0 :     GNUNET_PQ_make_execute (
   10529             :       "START TRANSACTION ISOLATION LEVEL READ COMMITTED;"),
   10530           0 :     GNUNET_PQ_make_execute ("SET CONSTRAINTS ALL DEFERRED;"),
   10531             :     GNUNET_PQ_EXECUTE_STATEMENT_END
   10532             :   };
   10533             : 
   10534           0 :   if (GNUNET_SYSERR ==
   10535           0 :       postgres_preflight (pg))
   10536           0 :     return GNUNET_SYSERR;
   10537           0 :   if (GNUNET_OK !=
   10538           0 :       GNUNET_PQ_exec_statements (pg->conn,
   10539             :                                  es))
   10540             :   {
   10541           0 :     TALER_LOG_ERROR (
   10542             :       "Failed to defer wire_out_ref constraint on transaction\n");
   10543           0 :     GNUNET_break (0);
   10544           0 :     postgres_rollback (pg);
   10545           0 :     return GNUNET_SYSERR;
   10546             :   }
   10547           0 :   pg->transaction_name = "deferred wire out";
   10548           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   10549             :               "Starting READ COMMITTED DEFERRED transaction `%s'\n",
   10550             :               pg->transaction_name);
   10551           0 :   return GNUNET_OK;
   10552             : }
   10553             : 
   10554             : 
   10555             : /**
   10556             :  * Store information about an outgoing wire transfer that was executed.
   10557             :  *
   10558             :  * @param cls closure
   10559             :  * @param date time of the wire transfer
   10560             :  * @param wtid subject of the wire transfer
   10561             :  * @param h_payto identifies the receiver account of the wire transfer
   10562             :  * @param exchange_account_section configuration section of the exchange specifying the
   10563             :  *        exchange's bank account being used
   10564             :  * @param amount amount that was transmitted
   10565             :  * @return transaction status code
   10566             :  */
   10567             : static enum GNUNET_DB_QueryStatus
   10568           0 : postgres_store_wire_transfer_out (
   10569             :   void *cls,
   10570             :   struct GNUNET_TIME_Timestamp date,
   10571             :   const struct TALER_WireTransferIdentifierRawP *wtid,
   10572             :   const struct TALER_PaytoHashP *h_payto,
   10573             :   const char *exchange_account_section,
   10574             :   const struct TALER_Amount *amount)
   10575             : {
   10576           0 :   struct PostgresClosure *pg = cls;
   10577           0 :   struct GNUNET_PQ_QueryParam params[] = {
   10578           0 :     GNUNET_PQ_query_param_timestamp (&date),
   10579           0 :     GNUNET_PQ_query_param_auto_from_type (wtid),
   10580           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
   10581           0 :     GNUNET_PQ_query_param_string (exchange_account_section),
   10582           0 :     TALER_PQ_query_param_amount (amount),
   10583             :     GNUNET_PQ_query_param_end
   10584             :   };
   10585             : 
   10586           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   10587             :                                              "insert_wire_out",
   10588             :                                              params);
   10589             : }
   10590             : 
   10591             : 
   10592             : /**
   10593             :  * Function called to perform "garbage collection" on the
   10594             :  * database, expiring records we no longer require.
   10595             :  *
   10596             :  * @param cls closure
   10597             :  * @return #GNUNET_OK on success,
   10598             :  *         #GNUNET_SYSERR on DB errors
   10599             :  */
   10600             : static enum GNUNET_GenericReturnValue
   10601           0 : postgres_gc (void *cls)
   10602             : {
   10603           0 :   struct PostgresClosure *pg = cls;
   10604           0 :   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
   10605             :   struct GNUNET_TIME_Absolute long_ago;
   10606           0 :   struct GNUNET_PQ_QueryParam params[] = {
   10607           0 :     GNUNET_PQ_query_param_absolute_time (&long_ago),
   10608           0 :     GNUNET_PQ_query_param_absolute_time (&now),
   10609             :     GNUNET_PQ_query_param_end
   10610             :   };
   10611             :   struct GNUNET_PQ_Context *conn;
   10612             :   enum GNUNET_GenericReturnValue ret;
   10613             : 
   10614             :   /* Keep wire fees for 10 years, that should always
   10615             :      be enough _and_ they are tiny so it does not
   10616             :      matter to make this tight */
   10617           0 :   long_ago = GNUNET_TIME_absolute_subtract (
   10618             :     now,
   10619             :     GNUNET_TIME_relative_multiply (
   10620             :       GNUNET_TIME_UNIT_YEARS,
   10621             :       10));
   10622             :   {
   10623           0 :     struct GNUNET_PQ_ExecuteStatement es[] = {
   10624           0 :       GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"),
   10625             :       GNUNET_PQ_EXECUTE_STATEMENT_END
   10626             :     };
   10627           0 :     struct GNUNET_PQ_PreparedStatement ps[] = {
   10628             :       /* Used in #postgres_gc() */
   10629           0 :       GNUNET_PQ_make_prepare ("run_gc",
   10630             :                               "CALL"
   10631             :                               " exchange_do_gc"
   10632             :                               " ($1,$2);",
   10633             :                               2),
   10634             :       GNUNET_PQ_PREPARED_STATEMENT_END
   10635             :     };
   10636             : 
   10637           0 :     conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
   10638             :                                        "exchangedb-postgres",
   10639             :                                        NULL,
   10640             :                                        es,
   10641             :                                        ps);
   10642             :   }
   10643           0 :   if (NULL == conn)
   10644           0 :     return GNUNET_SYSERR;
   10645           0 :   ret = GNUNET_OK;
   10646           0 :   if (0 > GNUNET_PQ_eval_prepared_non_select (conn,
   10647             :                                               "run_gc",
   10648             :                                               params))
   10649           0 :     ret = GNUNET_SYSERR;
   10650           0 :   GNUNET_PQ_disconnect (conn);
   10651           0 :   return ret;
   10652             : }
   10653             : 
   10654             : 
   10655             : /**
   10656             :  * Closure for #deposit_serial_helper_cb().
   10657             :  */
   10658             : struct DepositSerialContext
   10659             : {
   10660             : 
   10661             :   /**
   10662             :    * Callback to call.
   10663             :    */
   10664             :   TALER_EXCHANGEDB_DepositCallback cb;
   10665             : 
   10666             :   /**
   10667             :    * Closure for @e cb.
   10668             :    */
   10669             :   void *cb_cls;
   10670             : 
   10671             :   /**
   10672             :    * Plugin context.
   10673             :    */
   10674             :   struct PostgresClosure *pg;
   10675             : 
   10676             :   /**
   10677             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   10678             :    */
   10679             :   enum GNUNET_GenericReturnValue status;
   10680             : };
   10681             : 
   10682             : 
   10683             : /**
   10684             :  * Helper function to be called with the results of a SELECT statement
   10685             :  * that has returned @a num_results results.
   10686             :  *
   10687             :  * @param cls closure of type `struct DepositSerialContext`
   10688             :  * @param result the postgres result
   10689             :  * @param num_results the number of results in @a result
   10690             :  */
   10691             : static void
   10692           0 : deposit_serial_helper_cb (void *cls,
   10693             :                           PGresult *result,
   10694             :                           unsigned int num_results)
   10695             : {
   10696           0 :   struct DepositSerialContext *dsc = cls;
   10697           0 :   struct PostgresClosure *pg = dsc->pg;
   10698             : 
   10699           0 :   for (unsigned int i = 0; i<num_results; i++)
   10700             :   {
   10701             :     struct TALER_EXCHANGEDB_Deposit deposit;
   10702             :     struct GNUNET_TIME_Timestamp exchange_timestamp;
   10703             :     struct TALER_DenominationPublicKey denom_pub;
   10704             :     bool done;
   10705             :     uint64_t rowid;
   10706           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   10707           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   10708             :                                    &deposit.amount_with_fee),
   10709           0 :       GNUNET_PQ_result_spec_timestamp ("wallet_timestamp",
   10710             :                                        &deposit.timestamp),
   10711           0 :       GNUNET_PQ_result_spec_timestamp ("exchange_timestamp",
   10712             :                                        &exchange_timestamp),
   10713           0 :       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
   10714             :                                             &deposit.merchant_pub),
   10715           0 :       TALER_PQ_result_spec_denom_pub ("denom_pub",
   10716             :                                       &denom_pub),
   10717           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
   10718             :                                             &deposit.coin.coin_pub),
   10719           0 :       GNUNET_PQ_result_spec_allow_null (
   10720             :         GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
   10721             :                                               &deposit.coin.h_age_commitment),
   10722             :         &deposit.coin.no_age_commitment),
   10723           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
   10724             :                                             &deposit.csig),
   10725           0 :       GNUNET_PQ_result_spec_timestamp ("refund_deadline",
   10726             :                                        &deposit.refund_deadline),
   10727           0 :       GNUNET_PQ_result_spec_timestamp ("wire_deadline",
   10728             :                                        &deposit.wire_deadline),
   10729           0 :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
   10730             :                                             &deposit.h_contract_terms),
   10731           0 :       GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
   10732             :                                             &deposit.wire_salt),
   10733           0 :       GNUNET_PQ_result_spec_string ("receiver_wire_account",
   10734             :                                     &deposit.receiver_wire_account),
   10735           0 :       GNUNET_PQ_result_spec_bool ("done",
   10736             :                                   &done),
   10737           0 :       GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
   10738             :                                     &rowid),
   10739             :       GNUNET_PQ_result_spec_end
   10740             :     };
   10741             :     enum GNUNET_GenericReturnValue ret;
   10742             : 
   10743           0 :     memset (&deposit,
   10744             :             0,
   10745             :             sizeof (deposit));
   10746           0 :     if (GNUNET_OK !=
   10747           0 :         GNUNET_PQ_extract_result (result,
   10748             :                                   rs,
   10749             :                                   i))
   10750             :     {
   10751           0 :       GNUNET_break (0);
   10752           0 :       dsc->status = GNUNET_SYSERR;
   10753           0 :       return;
   10754             :     }
   10755           0 :     ret = dsc->cb (dsc->cb_cls,
   10756             :                    rowid,
   10757             :                    exchange_timestamp,
   10758             :                    &deposit,
   10759             :                    &denom_pub,
   10760             :                    done);
   10761           0 :     GNUNET_PQ_cleanup_result (rs);
   10762           0 :     if (GNUNET_OK != ret)
   10763           0 :       break;
   10764             :   }
   10765             : }
   10766             : 
   10767             : 
   10768             : /**
   10769             :  * Select deposits above @a serial_id in monotonically increasing
   10770             :  * order.
   10771             :  *
   10772             :  * @param cls closure
   10773             :  * @param serial_id highest serial ID to exclude (select strictly larger)
   10774             :  * @param cb function to call on each result
   10775             :  * @param cb_cls closure for @a cb
   10776             :  * @return transaction status code
   10777             :  */
   10778             : static enum GNUNET_DB_QueryStatus
   10779           0 : postgres_select_deposits_above_serial_id (
   10780             :   void *cls,
   10781             :   uint64_t serial_id,
   10782             :   TALER_EXCHANGEDB_DepositCallback cb,
   10783             :   void *cb_cls)
   10784             : {
   10785           0 :   struct PostgresClosure *pg = cls;
   10786           0 :   struct GNUNET_PQ_QueryParam params[] = {
   10787           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   10788             :     GNUNET_PQ_query_param_end
   10789             :   };
   10790           0 :   struct DepositSerialContext dsc = {
   10791             :     .cb = cb,
   10792             :     .cb_cls = cb_cls,
   10793             :     .pg = pg,
   10794             :     .status = GNUNET_OK
   10795             :   };
   10796             :   enum GNUNET_DB_QueryStatus qs;
   10797             : 
   10798           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   10799             :                                              "audit_get_deposits_incr",
   10800             :                                              params,
   10801             :                                              &deposit_serial_helper_cb,
   10802             :                                              &dsc);
   10803           0 :   if (GNUNET_OK != dsc.status)
   10804           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   10805           0 :   return qs;
   10806             : }
   10807             : 
   10808             : 
   10809             : /**
   10810             :  * Closure for #purse_deposit_serial_helper_cb().
   10811             :  */
   10812             : struct PurseDepositSerialContext
   10813             : {
   10814             : 
   10815             :   /**
   10816             :    * Callback to call.
   10817             :    */
   10818             :   TALER_EXCHANGEDB_PurseDepositCallback cb;
   10819             : 
   10820             :   /**
   10821             :    * Closure for @e cb.
   10822             :    */
   10823             :   void *cb_cls;
   10824             : 
   10825             :   /**
   10826             :    * Plugin context.
   10827             :    */
   10828             :   struct PostgresClosure *pg;
   10829             : 
   10830             :   /**
   10831             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   10832             :    */
   10833             :   enum GNUNET_GenericReturnValue status;
   10834             : };
   10835             : 
   10836             : 
   10837             : /**
   10838             :  * Helper function to be called with the results of a SELECT statement
   10839             :  * that has returned @a num_results results.
   10840             :  *
   10841             :  * @param cls closure of type `struct DepositSerialContext`
   10842             :  * @param result the postgres result
   10843             :  * @param num_results the number of results in @a result
   10844             :  */
   10845             : static void
   10846           0 : purse_deposit_serial_helper_cb (void *cls,
   10847             :                                 PGresult *result,
   10848             :                                 unsigned int num_results)
   10849             : {
   10850           0 :   struct PurseDepositSerialContext *dsc = cls;
   10851           0 :   struct PostgresClosure *pg = dsc->pg;
   10852             : 
   10853           0 :   for (unsigned int i = 0; i<num_results; i++)
   10854             :   {
   10855           0 :     struct TALER_EXCHANGEDB_PurseDeposit deposit = {
   10856             :       .exchange_base_url = NULL
   10857             :     };
   10858             :     struct TALER_DenominationPublicKey denom_pub;
   10859             :     uint64_t rowid;
   10860             :     uint32_t flags32;
   10861             :     struct TALER_ReservePublicKeyP reserve_pub;
   10862           0 :     bool not_merged = false;
   10863             :     struct TALER_Amount purse_balance;
   10864             :     struct TALER_Amount purse_total;
   10865           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   10866           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   10867             :                                    &deposit.amount),
   10868           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
   10869             :                                    &purse_balance),
   10870           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("total",
   10871             :                                    &purse_total),
   10872           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
   10873             :                                    &deposit.deposit_fee),
   10874           0 :       GNUNET_PQ_result_spec_allow_null (
   10875             :         GNUNET_PQ_result_spec_string ("partner_base_url",
   10876             :                                       &deposit.exchange_base_url),
   10877             :         NULL),
   10878           0 :       GNUNET_PQ_result_spec_allow_null (
   10879             :         GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
   10880             :                                               &reserve_pub),
   10881             :         &not_merged),
   10882           0 :       TALER_PQ_result_spec_denom_pub ("denom_pub",
   10883             :                                       &denom_pub),
   10884           0 :       GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
   10885             :                                             &deposit.purse_pub),
   10886           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
   10887             :                                             &deposit.coin_sig),
   10888           0 :       GNUNET_PQ_result_spec_uint32 ("flags",
   10889             :                                     &flags32),
   10890           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
   10891             :                                             &deposit.coin_pub),
   10892           0 :       GNUNET_PQ_result_spec_allow_null (
   10893             :         GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
   10894             :                                               &deposit.h_age_commitment),
   10895             :         &deposit.no_age_commitment),
   10896           0 :       GNUNET_PQ_result_spec_uint64 ("purse_deposit_serial_id",
   10897             :                                     &rowid),
   10898             :       GNUNET_PQ_result_spec_end
   10899             :     };
   10900             :     enum GNUNET_GenericReturnValue ret;
   10901             : 
   10902           0 :     memset (&deposit,
   10903             :             0,
   10904             :             sizeof (deposit));
   10905           0 :     if (GNUNET_OK !=
   10906           0 :         GNUNET_PQ_extract_result (result,
   10907             :                                   rs,
   10908             :                                   i))
   10909             :     {
   10910           0 :       GNUNET_break (0);
   10911           0 :       dsc->status = GNUNET_SYSERR;
   10912           0 :       return;
   10913             :     }
   10914           0 :     ret = dsc->cb (dsc->cb_cls,
   10915             :                    rowid,
   10916             :                    &deposit,
   10917             :                    not_merged ? NULL : &reserve_pub,
   10918             :                    (enum TALER_WalletAccountMergeFlags) flags32,
   10919             :                    &purse_balance,
   10920             :                    &purse_total,
   10921             :                    &denom_pub);
   10922           0 :     GNUNET_PQ_cleanup_result (rs);
   10923           0 :     if (GNUNET_OK != ret)
   10924           0 :       break;
   10925             :   }
   10926             : }
   10927             : 
   10928             : 
   10929             : /**
   10930             :  * Select deposits above @a serial_id in monotonically increasing
   10931             :  * order.
   10932             :  *
   10933             :  * @param cls closure
   10934             :  * @param serial_id highest serial ID to exclude (select strictly larger)
   10935             :  * @param cb function to call on each result
   10936             :  * @param cb_cls closure for @a cb
   10937             :  * @return transaction status code
   10938             :  */
   10939             : static enum GNUNET_DB_QueryStatus
   10940           0 : postgres_select_purse_deposits_above_serial_id (
   10941             :   void *cls,
   10942             :   uint64_t serial_id,
   10943             :   TALER_EXCHANGEDB_PurseDepositCallback cb,
   10944             :   void *cb_cls)
   10945             : {
   10946           0 :   struct PostgresClosure *pg = cls;
   10947           0 :   struct GNUNET_PQ_QueryParam params[] = {
   10948           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   10949             :     GNUNET_PQ_query_param_end
   10950             :   };
   10951           0 :   struct PurseDepositSerialContext dsc = {
   10952             :     .cb = cb,
   10953             :     .cb_cls = cb_cls,
   10954             :     .pg = pg,
   10955             :     .status = GNUNET_OK
   10956             :   };
   10957             :   enum GNUNET_DB_QueryStatus qs;
   10958             : 
   10959           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   10960             :                                              "audit_get_purse_deposits_incr",
   10961             :                                              params,
   10962             :                                              &purse_deposit_serial_helper_cb,
   10963             :                                              &dsc);
   10964           0 :   if (GNUNET_OK != dsc.status)
   10965           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   10966           0 :   return qs;
   10967             : }
   10968             : 
   10969             : 
   10970             : /**
   10971             :  * Closure for #account_merge_serial_helper_cb().
   10972             :  */
   10973             : struct AccountMergeSerialContext
   10974             : {
   10975             : 
   10976             :   /**
   10977             :    * Callback to call.
   10978             :    */
   10979             :   TALER_EXCHANGEDB_AccountMergeCallback cb;
   10980             : 
   10981             :   /**
   10982             :    * Closure for @e cb.
   10983             :    */
   10984             :   void *cb_cls;
   10985             : 
   10986             :   /**
   10987             :    * Plugin context.
   10988             :    */
   10989             :   struct PostgresClosure *pg;
   10990             : 
   10991             :   /**
   10992             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   10993             :    */
   10994             :   enum GNUNET_GenericReturnValue status;
   10995             : };
   10996             : 
   10997             : 
   10998             : /**
   10999             :  * Helper function to be called with the results of a SELECT statement
   11000             :  * that has returned @a num_results results.
   11001             :  *
   11002             :  * @param cls closure of type `struct AccountMergeSerialContext`
   11003             :  * @param result the postgres result
   11004             :  * @param num_results the number of results in @a result
   11005             :  */
   11006             : static void
   11007           0 : account_merge_serial_helper_cb (void *cls,
   11008             :                                 PGresult *result,
   11009             :                                 unsigned int num_results)
   11010             : {
   11011           0 :   struct AccountMergeSerialContext *dsc = cls;
   11012           0 :   struct PostgresClosure *pg = dsc->pg;
   11013             : 
   11014           0 :   for (unsigned int i = 0; i<num_results; i++)
   11015             :   {
   11016             :     struct TALER_ReservePublicKeyP reserve_pub;
   11017             :     struct TALER_PurseContractPublicKeyP purse_pub;
   11018             :     struct TALER_PrivateContractHashP h_contract_terms;
   11019             :     struct GNUNET_TIME_Timestamp purse_expiration;
   11020             :     struct TALER_Amount amount;
   11021             :     uint32_t min_age;
   11022             :     uint32_t flags32;
   11023             :     enum TALER_WalletAccountMergeFlags flags;
   11024             :     struct TALER_Amount purse_fee;
   11025             :     struct GNUNET_TIME_Timestamp merge_timestamp;
   11026             :     struct TALER_ReserveSignatureP reserve_sig;
   11027             :     uint64_t rowid;
   11028           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   11029           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   11030             :                                    &amount),
   11031           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee",
   11032             :                                    &purse_fee),
   11033           0 :       GNUNET_PQ_result_spec_uint32 ("flags",
   11034             :                                     &flags32),
   11035           0 :       GNUNET_PQ_result_spec_uint32 ("age_limit",
   11036             :                                     &min_age),
   11037           0 :       GNUNET_PQ_result_spec_timestamp ("purse_expiration",
   11038             :                                        &purse_expiration),
   11039           0 :       GNUNET_PQ_result_spec_timestamp ("merge_timestamp",
   11040             :                                        &merge_timestamp),
   11041           0 :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
   11042             :                                             &h_contract_terms),
   11043           0 :       GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
   11044             :                                             &purse_pub),
   11045           0 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
   11046             :                                             &reserve_sig),
   11047           0 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
   11048             :                                             &reserve_pub),
   11049           0 :       GNUNET_PQ_result_spec_uint64 ("account_merge_request_serial_id",
   11050             :                                     &rowid),
   11051             :       GNUNET_PQ_result_spec_end
   11052             :     };
   11053             :     enum GNUNET_GenericReturnValue ret;
   11054             : 
   11055           0 :     if (GNUNET_OK !=
   11056           0 :         GNUNET_PQ_extract_result (result,
   11057             :                                   rs,
   11058             :                                   i))
   11059             :     {
   11060           0 :       GNUNET_break (0);
   11061           0 :       dsc->status = GNUNET_SYSERR;
   11062           0 :       return;
   11063             :     }
   11064           0 :     flags = (enum TALER_WalletAccountMergeFlags) flags32;
   11065           0 :     ret = dsc->cb (dsc->cb_cls,
   11066             :                    rowid,
   11067             :                    &reserve_pub,
   11068             :                    &purse_pub,
   11069             :                    &h_contract_terms,
   11070             :                    purse_expiration,
   11071             :                    &amount,
   11072             :                    min_age,
   11073             :                    flags,
   11074             :                    &purse_fee,
   11075             :                    merge_timestamp,
   11076             :                    &reserve_sig);
   11077           0 :     GNUNET_PQ_cleanup_result (rs);
   11078           0 :     if (GNUNET_OK != ret)
   11079           0 :       break;
   11080             :   }
   11081             : }
   11082             : 
   11083             : 
   11084             : /**
   11085             :  * Select account merges above @a serial_id in monotonically increasing
   11086             :  * order.
   11087             :  *
   11088             :  * @param cls closure
   11089             :  * @param serial_id highest serial ID to exclude (select strictly larger)
   11090             :  * @param cb function to call on each result
   11091             :  * @param cb_cls closure for @a cb
   11092             :  * @return transaction status code
   11093             :  */
   11094             : static enum GNUNET_DB_QueryStatus
   11095           0 : postgres_select_account_merges_above_serial_id (
   11096             :   void *cls,
   11097             :   uint64_t serial_id,
   11098             :   TALER_EXCHANGEDB_AccountMergeCallback cb,
   11099             :   void *cb_cls)
   11100             : {
   11101           0 :   struct PostgresClosure *pg = cls;
   11102           0 :   struct GNUNET_PQ_QueryParam params[] = {
   11103           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   11104             :     GNUNET_PQ_query_param_end
   11105             :   };
   11106           0 :   struct AccountMergeSerialContext dsc = {
   11107             :     .cb = cb,
   11108             :     .cb_cls = cb_cls,
   11109             :     .pg = pg,
   11110             :     .status = GNUNET_OK
   11111             :   };
   11112             :   enum GNUNET_DB_QueryStatus qs;
   11113             : 
   11114           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   11115             :                                              "audit_get_account_merge_incr",
   11116             :                                              params,
   11117             :                                              &account_merge_serial_helper_cb,
   11118             :                                              &dsc);
   11119           0 :   if (GNUNET_OK != dsc.status)
   11120           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   11121           0 :   return qs;
   11122             : }
   11123             : 
   11124             : 
   11125             : /**
   11126             :  * Closure for #purse_deposit_serial_helper_cb().
   11127             :  */
   11128             : struct PurseMergeSerialContext
   11129             : {
   11130             : 
   11131             :   /**
   11132             :    * Callback to call.
   11133             :    */
   11134             :   TALER_EXCHANGEDB_PurseMergeCallback cb;
   11135             : 
   11136             :   /**
   11137             :    * Closure for @e cb.
   11138             :    */
   11139             :   void *cb_cls;
   11140             : 
   11141             :   /**
   11142             :    * Plugin context.
   11143             :    */
   11144             :   struct PostgresClosure *pg;
   11145             : 
   11146             :   /**
   11147             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   11148             :    */
   11149             :   enum GNUNET_GenericReturnValue status;
   11150             : };
   11151             : 
   11152             : 
   11153             : /**
   11154             :  * Helper function to be called with the results of a SELECT statement
   11155             :  * that has returned @a num_results results.
   11156             :  *
   11157             :  * @param cls closure of type `struct PurseMergeSerialContext`
   11158             :  * @param result the postgres result
   11159             :  * @param num_results the number of results in @a result
   11160             :  */
   11161             : static void
   11162           0 : purse_merges_serial_helper_cb (void *cls,
   11163             :                                PGresult *result,
   11164             :                                unsigned int num_results)
   11165             : {
   11166           0 :   struct PurseMergeSerialContext *dsc = cls;
   11167           0 :   struct PostgresClosure *pg = dsc->pg;
   11168             : 
   11169           0 :   for (unsigned int i = 0; i<num_results; i++)
   11170             :   {
   11171             :     uint64_t rowid;
   11172           0 :     char *partner_base_url = NULL;
   11173             :     struct TALER_Amount amount;
   11174             :     struct TALER_Amount balance;
   11175             :     uint32_t flags32;
   11176             :     enum TALER_WalletAccountMergeFlags flags;
   11177             :     struct TALER_PurseMergePublicKeyP merge_pub;
   11178             :     struct TALER_ReservePublicKeyP reserve_pub;
   11179             :     struct TALER_PurseMergeSignatureP merge_sig;
   11180             :     struct TALER_PurseContractPublicKeyP purse_pub;
   11181             :     struct GNUNET_TIME_Timestamp merge_timestamp;
   11182           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   11183           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   11184             :                                    &amount),
   11185           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
   11186             :                                    &balance),
   11187           0 :       GNUNET_PQ_result_spec_allow_null (
   11188             :         GNUNET_PQ_result_spec_string ("partner_base_url",
   11189             :                                       &partner_base_url),
   11190             :         NULL),
   11191           0 :       GNUNET_PQ_result_spec_uint32 ("flags",
   11192             :                                     &flags32),
   11193           0 :       GNUNET_PQ_result_spec_timestamp ("merge_timestamp",
   11194             :                                        &merge_timestamp),
   11195           0 :       GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
   11196             :                                             &purse_pub),
   11197           0 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
   11198             :                                             &reserve_pub),
   11199           0 :       GNUNET_PQ_result_spec_auto_from_type ("merge_sig",
   11200             :                                             &merge_sig),
   11201           0 :       GNUNET_PQ_result_spec_auto_from_type ("merge_pub",
   11202             :                                             &merge_pub),
   11203           0 :       GNUNET_PQ_result_spec_uint64 ("purse_merge_request_serial_id",
   11204             :                                     &rowid),
   11205             :       GNUNET_PQ_result_spec_end
   11206             :     };
   11207             :     enum GNUNET_GenericReturnValue ret;
   11208             : 
   11209           0 :     if (GNUNET_OK !=
   11210           0 :         GNUNET_PQ_extract_result (result,
   11211             :                                   rs,
   11212             :                                   i))
   11213             :     {
   11214           0 :       GNUNET_break (0);
   11215           0 :       dsc->status = GNUNET_SYSERR;
   11216           0 :       return;
   11217             :     }
   11218           0 :     flags = (enum TALER_WalletAccountMergeFlags) flags32;
   11219           0 :     ret = dsc->cb (dsc->cb_cls,
   11220             :                    rowid,
   11221             :                    partner_base_url,
   11222             :                    &amount,
   11223             :                    &balance,
   11224             :                    flags,
   11225             :                    &merge_pub,
   11226             :                    &reserve_pub,
   11227             :                    &merge_sig,
   11228             :                    &purse_pub,
   11229             :                    merge_timestamp);
   11230           0 :     GNUNET_PQ_cleanup_result (rs);
   11231           0 :     if (GNUNET_OK != ret)
   11232           0 :       break;
   11233             :   }
   11234             : }
   11235             : 
   11236             : 
   11237             : /**
   11238             :  * Select purse merges deposits above @a serial_id in monotonically increasing
   11239             :  * order.
   11240             :  *
   11241             :  * @param cls closure
   11242             :  * @param serial_id highest serial ID to exclude (select strictly larger)
   11243             :  * @param cb function to call on each result
   11244             :  * @param cb_cls closure for @a cb
   11245             :  * @return transaction status code
   11246             :  */
   11247             : static enum GNUNET_DB_QueryStatus
   11248           0 : postgres_select_purse_merges_above_serial_id (
   11249             :   void *cls,
   11250             :   uint64_t serial_id,
   11251             :   TALER_EXCHANGEDB_PurseMergeCallback cb,
   11252             :   void *cb_cls)
   11253             : {
   11254           0 :   struct PostgresClosure *pg = cls;
   11255           0 :   struct GNUNET_PQ_QueryParam params[] = {
   11256           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   11257             :     GNUNET_PQ_query_param_end
   11258             :   };
   11259           0 :   struct PurseMergeSerialContext dsc = {
   11260             :     .cb = cb,
   11261             :     .cb_cls = cb_cls,
   11262             :     .pg = pg,
   11263             :     .status = GNUNET_OK
   11264             :   };
   11265             :   enum GNUNET_DB_QueryStatus qs;
   11266             : 
   11267           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   11268             :                                              "audit_get_purse_merge_incr",
   11269             :                                              params,
   11270             :                                              &purse_merges_serial_helper_cb,
   11271             :                                              &dsc);
   11272           0 :   if (GNUNET_OK != dsc.status)
   11273           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   11274           0 :   return qs;
   11275             : }
   11276             : 
   11277             : 
   11278             : /**
   11279             :  * Closure for #purse_deposit_serial_helper_cb().
   11280             :  */
   11281             : struct HistoryRequestSerialContext
   11282             : {
   11283             : 
   11284             :   /**
   11285             :    * Callback to call.
   11286             :    */
   11287             :   TALER_EXCHANGEDB_HistoryRequestCallback cb;
   11288             : 
   11289             :   /**
   11290             :    * Closure for @e cb.
   11291             :    */
   11292             :   void *cb_cls;
   11293             : 
   11294             :   /**
   11295             :    * Plugin context.
   11296             :    */
   11297             :   struct PostgresClosure *pg;
   11298             : 
   11299             :   /**
   11300             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   11301             :    */
   11302             :   enum GNUNET_GenericReturnValue status;
   11303             : };
   11304             : 
   11305             : 
   11306             : /**
   11307             :  * Helper function to be called with the results of a SELECT statement
   11308             :  * that has returned @a num_results results.
   11309             :  *
   11310             :  * @param cls closure of type `struct HistoryRequestSerialContext`
   11311             :  * @param result the postgres result
   11312             :  * @param num_results the number of results in @a result
   11313             :  */
   11314             : static void
   11315           0 : history_request_serial_helper_cb (void *cls,
   11316             :                                   PGresult *result,
   11317             :                                   unsigned int num_results)
   11318             : {
   11319           0 :   struct HistoryRequestSerialContext *dsc = cls;
   11320           0 :   struct PostgresClosure *pg = dsc->pg;
   11321             : 
   11322           0 :   for (unsigned int i = 0; i<num_results; i++)
   11323             :   {
   11324             :     uint64_t rowid;
   11325             :     struct TALER_Amount history_fee;
   11326             :     struct GNUNET_TIME_Timestamp ts;
   11327             :     struct TALER_ReservePublicKeyP reserve_pub;
   11328             :     struct TALER_ReserveSignatureP reserve_sig;
   11329           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   11330           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
   11331             :                                    &history_fee),
   11332           0 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
   11333             :                                             &reserve_pub),
   11334           0 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
   11335             :                                             &reserve_sig),
   11336           0 :       GNUNET_PQ_result_spec_uint64 ("history_request_serial_id",
   11337             :                                     &rowid),
   11338           0 :       GNUNET_PQ_result_spec_timestamp ("request_timestamp",
   11339             :                                        &ts),
   11340             :       GNUNET_PQ_result_spec_end
   11341             :     };
   11342             :     enum GNUNET_GenericReturnValue ret;
   11343             : 
   11344           0 :     if (GNUNET_OK !=
   11345           0 :         GNUNET_PQ_extract_result (result,
   11346             :                                   rs,
   11347             :                                   i))
   11348             :     {
   11349           0 :       GNUNET_break (0);
   11350           0 :       dsc->status = GNUNET_SYSERR;
   11351           0 :       return;
   11352             :     }
   11353           0 :     ret = dsc->cb (dsc->cb_cls,
   11354             :                    rowid,
   11355             :                    &history_fee,
   11356             :                    ts,
   11357             :                    &reserve_pub,
   11358             :                    &reserve_sig);
   11359           0 :     GNUNET_PQ_cleanup_result (rs);
   11360           0 :     if (GNUNET_OK != ret)
   11361           0 :       break;
   11362             :   }
   11363             : }
   11364             : 
   11365             : 
   11366             : /**
   11367             :  * Select history requests above @a serial_id in monotonically increasing
   11368             :  * order.
   11369             :  *
   11370             :  * @param cls closure
   11371             :  * @param serial_id highest serial ID to exclude (select strictly larger)
   11372             :  * @param cb function to call on each result
   11373             :  * @param cb_cls closure for @a cb
   11374             :  * @return transaction status code
   11375             :  */
   11376             : static enum GNUNET_DB_QueryStatus
   11377           0 : postgres_select_history_requests_above_serial_id (
   11378             :   void *cls,
   11379             :   uint64_t serial_id,
   11380             :   TALER_EXCHANGEDB_HistoryRequestCallback cb,
   11381             :   void *cb_cls)
   11382             : {
   11383           0 :   struct PostgresClosure *pg = cls;
   11384           0 :   struct GNUNET_PQ_QueryParam params[] = {
   11385           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   11386             :     GNUNET_PQ_query_param_end
   11387             :   };
   11388           0 :   struct HistoryRequestSerialContext dsc = {
   11389             :     .cb = cb,
   11390             :     .cb_cls = cb_cls,
   11391             :     .pg = pg,
   11392             :     .status = GNUNET_OK
   11393             :   };
   11394             :   enum GNUNET_DB_QueryStatus qs;
   11395             : 
   11396           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   11397             :                                              "audit_get_history_requests_incr",
   11398             :                                              params,
   11399             :                                              &history_request_serial_helper_cb,
   11400             :                                              &dsc);
   11401           0 :   if (GNUNET_OK != dsc.status)
   11402           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   11403           0 :   return qs;
   11404             : }
   11405             : 
   11406             : 
   11407             : /**
   11408             :  * Closure for #purse_refund_serial_helper_cb().
   11409             :  */
   11410             : struct PurseRefundSerialContext
   11411             : {
   11412             : 
   11413             :   /**
   11414             :    * Callback to call.
   11415             :    */
   11416             :   TALER_EXCHANGEDB_PurseRefundCallback cb;
   11417             : 
   11418             :   /**
   11419             :    * Closure for @e cb.
   11420             :    */
   11421             :   void *cb_cls;
   11422             : 
   11423             :   /**
   11424             :    * Plugin context.
   11425             :    */
   11426             :   struct PostgresClosure *pg;
   11427             : 
   11428             :   /**
   11429             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   11430             :    */
   11431             :   enum GNUNET_GenericReturnValue status;
   11432             : };
   11433             : 
   11434             : 
   11435             : /**
   11436             :  * Helper function to be called with the results of a SELECT statement
   11437             :  * that has returned @a num_results results.
   11438             :  *
   11439             :  * @param cls closure of type `struct PurseRefundSerialContext`
   11440             :  * @param result the postgres result
   11441             :  * @param num_results the number of results in @a result
   11442             :  */
   11443             : static void
   11444           0 : purse_refund_serial_helper_cb (void *cls,
   11445             :                                PGresult *result,
   11446             :                                unsigned int num_results)
   11447             : {
   11448           0 :   struct PurseRefundSerialContext *dsc = cls;
   11449             : 
   11450           0 :   for (unsigned int i = 0; i<num_results; i++)
   11451             :   {
   11452             :     struct TALER_PurseContractPublicKeyP purse_pub;
   11453             :     uint64_t rowid;
   11454           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   11455           0 :       GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
   11456             :                                             &purse_pub),
   11457           0 :       GNUNET_PQ_result_spec_uint64 ("purse_deposit_serial_id",
   11458             :                                     &rowid),
   11459             :       GNUNET_PQ_result_spec_end
   11460             :     };
   11461             :     enum GNUNET_GenericReturnValue ret;
   11462             : 
   11463           0 :     if (GNUNET_OK !=
   11464           0 :         GNUNET_PQ_extract_result (result,
   11465             :                                   rs,
   11466             :                                   i))
   11467             :     {
   11468           0 :       GNUNET_break (0);
   11469           0 :       dsc->status = GNUNET_SYSERR;
   11470           0 :       return;
   11471             :     }
   11472           0 :     ret = dsc->cb (dsc->cb_cls,
   11473             :                    rowid,
   11474             :                    &purse_pub);
   11475           0 :     GNUNET_PQ_cleanup_result (rs);
   11476           0 :     if (GNUNET_OK != ret)
   11477           0 :       break;
   11478             :   }
   11479             : }
   11480             : 
   11481             : 
   11482             : /**
   11483             :  * Select purse refunds above @a serial_id in monotonically increasing
   11484             :  * order.
   11485             :  *
   11486             :  * @param cls closure
   11487             :  * @param serial_id highest serial ID to exclude (select strictly larger)
   11488             :  * @param cb function to call on each result
   11489             :  * @param cb_cls closure for @a cb
   11490             :  * @return transaction status code
   11491             :  */
   11492             : static enum GNUNET_DB_QueryStatus
   11493           0 : postgres_select_purse_refunds_above_serial_id (
   11494             :   void *cls,
   11495             :   uint64_t serial_id,
   11496             :   TALER_EXCHANGEDB_PurseRefundCallback cb,
   11497             :   void *cb_cls)
   11498             : {
   11499           0 :   struct PostgresClosure *pg = cls;
   11500           0 :   struct GNUNET_PQ_QueryParam params[] = {
   11501           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   11502             :     GNUNET_PQ_query_param_end
   11503             :   };
   11504           0 :   struct PurseRefundSerialContext dsc = {
   11505             :     .cb = cb,
   11506             :     .cb_cls = cb_cls,
   11507             :     .pg = pg,
   11508             :     .status = GNUNET_OK
   11509             :   };
   11510             :   enum GNUNET_DB_QueryStatus qs;
   11511             : 
   11512           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   11513             :                                              "audit_get_purse_refunds_incr",
   11514             :                                              params,
   11515             :                                              &purse_refund_serial_helper_cb,
   11516             :                                              &dsc);
   11517           0 :   if (GNUNET_OK != dsc.status)
   11518           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   11519           0 :   return qs;
   11520             : }
   11521             : 
   11522             : 
   11523             : /**
   11524             :  * Closure for #purse_refund_coin_helper_cb().
   11525             :  */
   11526             : struct PurseRefundCoinContext
   11527             : {
   11528             : 
   11529             :   /**
   11530             :    * Callback to call.
   11531             :    */
   11532             :   TALER_EXCHANGEDB_PurseRefundCoinCallback cb;
   11533             : 
   11534             :   /**
   11535             :    * Closure for @e cb.
   11536             :    */
   11537             :   void *cb_cls;
   11538             : 
   11539             :   /**
   11540             :    * Plugin context.
   11541             :    */
   11542             :   struct PostgresClosure *pg;
   11543             : 
   11544             :   /**
   11545             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   11546             :    */
   11547             :   enum GNUNET_GenericReturnValue status;
   11548             : };
   11549             : 
   11550             : 
   11551             : /**
   11552             :  * Helper function to be called with the results of a SELECT statement
   11553             :  * that has returned @a num_results results.
   11554             :  *
   11555             :  * @param cls closure of type `struct PurseRefundCoinContext`
   11556             :  * @param result the postgres result
   11557             :  * @param num_results the number of results in @a result
   11558             :  */
   11559             : static void
   11560           0 : purse_refund_coin_helper_cb (void *cls,
   11561             :                              PGresult *result,
   11562             :                              unsigned int num_results)
   11563             : {
   11564           0 :   struct PurseRefundCoinContext *dsc = cls;
   11565           0 :   struct PostgresClosure *pg = dsc->pg;
   11566             : 
   11567           0 :   for (unsigned int i = 0; i<num_results; i++)
   11568             :   {
   11569             :     struct TALER_Amount amount_with_fee;
   11570             :     struct TALER_CoinSpendPublicKeyP coin_pub;
   11571             :     struct TALER_DenominationPublicKey denom_pub;
   11572             :     uint64_t rowid;
   11573           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   11574           0 :       TALER_PQ_result_spec_denom_pub ("denom_pub",
   11575             :                                       &denom_pub),
   11576           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   11577             :                                    &amount_with_fee),
   11578           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
   11579             :                                             &coin_pub),
   11580           0 :       GNUNET_PQ_result_spec_uint64 ("purse_deposit_serial_id",
   11581             :                                     &rowid),
   11582             :       GNUNET_PQ_result_spec_end
   11583             :     };
   11584             :     enum GNUNET_GenericReturnValue ret;
   11585             : 
   11586           0 :     if (GNUNET_OK !=
   11587           0 :         GNUNET_PQ_extract_result (result,
   11588             :                                   rs,
   11589             :                                   i))
   11590             :     {
   11591           0 :       GNUNET_break (0);
   11592           0 :       dsc->status = GNUNET_SYSERR;
   11593           0 :       return;
   11594             :     }
   11595           0 :     ret = dsc->cb (dsc->cb_cls,
   11596             :                    rowid,
   11597             :                    &amount_with_fee,
   11598             :                    &coin_pub,
   11599             :                    &denom_pub);
   11600           0 :     GNUNET_PQ_cleanup_result (rs);
   11601           0 :     if (GNUNET_OK != ret)
   11602           0 :       break;
   11603             :   }
   11604             : }
   11605             : 
   11606             : 
   11607             : /**
   11608             :  * Select coin affected by purse refund.
   11609             :  *
   11610             :  * @param cls closure
   11611             :  * @param purse_pub purse that was refunded
   11612             :  * @param cb function to call on each result
   11613             :  * @param cb_cls closure for @a cb
   11614             :  * @return transaction status code
   11615             :  */
   11616             : static enum GNUNET_DB_QueryStatus
   11617           0 : postgres_select_purse_deposits_by_purse (
   11618             :   void *cls,
   11619             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
   11620             :   TALER_EXCHANGEDB_PurseRefundCoinCallback cb,
   11621             :   void *cb_cls)
   11622             : {
   11623           0 :   struct PostgresClosure *pg = cls;
   11624           0 :   struct GNUNET_PQ_QueryParam params[] = {
   11625           0 :     GNUNET_PQ_query_param_auto_from_type (purse_pub),
   11626             :     GNUNET_PQ_query_param_end
   11627             :   };
   11628           0 :   struct PurseRefundCoinContext dsc = {
   11629             :     .cb = cb,
   11630             :     .cb_cls = cb_cls,
   11631             :     .pg = pg,
   11632             :     .status = GNUNET_OK
   11633             :   };
   11634             :   enum GNUNET_DB_QueryStatus qs;
   11635             : 
   11636           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   11637             :                                              "audit_get_purse_deposits_by_purse",
   11638             :                                              params,
   11639             :                                              &purse_refund_coin_helper_cb,
   11640             :                                              &dsc);
   11641           0 :   if (GNUNET_OK != dsc.status)
   11642           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   11643           0 :   return qs;
   11644             : }
   11645             : 
   11646             : 
   11647             : /**
   11648             :  * Closure for #refreshs_serial_helper_cb().
   11649             :  */
   11650             : struct RefreshsSerialContext
   11651             : {
   11652             : 
   11653             :   /**
   11654             :    * Callback to call.
   11655             :    */
   11656             :   TALER_EXCHANGEDB_RefreshesCallback cb;
   11657             : 
   11658             :   /**
   11659             :    * Closure for @e cb.
   11660             :    */
   11661             :   void *cb_cls;
   11662             : 
   11663             :   /**
   11664             :    * Plugin context.
   11665             :    */
   11666             :   struct PostgresClosure *pg;
   11667             : 
   11668             :   /**
   11669             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   11670             :    */
   11671             :   enum GNUNET_GenericReturnValue status;
   11672             : };
   11673             : 
   11674             : 
   11675             : /**
   11676             :  * Helper function to be called with the results of a SELECT statement
   11677             :  * that has returned @a num_results results.
   11678             :  *
   11679             :  * @param cls closure of type `struct RefreshsSerialContext`
   11680             :  * @param result the postgres result
   11681             :  * @param num_results the number of results in @a result
   11682             :  */
   11683             : static void
   11684           0 : refreshs_serial_helper_cb (void *cls,
   11685             :                            PGresult *result,
   11686             :                            unsigned int num_results)
   11687             : {
   11688           0 :   struct RefreshsSerialContext *rsc = cls;
   11689           0 :   struct PostgresClosure *pg = rsc->pg;
   11690             : 
   11691           0 :   for (unsigned int i = 0; i<num_results; i++)
   11692             :   {
   11693             :     struct TALER_DenominationPublicKey denom_pub;
   11694             :     struct TALER_CoinSpendPublicKeyP coin_pub;
   11695             :     struct TALER_CoinSpendSignatureP coin_sig;
   11696             :     struct TALER_AgeCommitmentHash h_age_commitment;
   11697             :     bool ac_isnull;
   11698             :     struct TALER_Amount amount_with_fee;
   11699             :     uint32_t noreveal_index;
   11700             :     uint64_t rowid;
   11701             :     struct TALER_RefreshCommitmentP rc;
   11702           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   11703           0 :       TALER_PQ_result_spec_denom_pub ("denom_pub",
   11704             :                                       &denom_pub),
   11705           0 :       GNUNET_PQ_result_spec_allow_null (
   11706             :         GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
   11707             :                                               &h_age_commitment),
   11708             :         &ac_isnull),
   11709           0 :       GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
   11710             :                                             &coin_pub),
   11711           0 :       GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
   11712             :                                             &coin_sig),
   11713           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   11714             :                                    &amount_with_fee),
   11715           0 :       GNUNET_PQ_result_spec_uint32 ("noreveal_index",
   11716             :                                     &noreveal_index),
   11717           0 :       GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
   11718             :                                     &rowid),
   11719           0 :       GNUNET_PQ_result_spec_auto_from_type ("rc",
   11720             :                                             &rc),
   11721             :       GNUNET_PQ_result_spec_end
   11722             :     };
   11723             :     enum GNUNET_GenericReturnValue ret;
   11724             : 
   11725           0 :     if (GNUNET_OK !=
   11726           0 :         GNUNET_PQ_extract_result (result,
   11727             :                                   rs,
   11728             :                                   i))
   11729             :     {
   11730           0 :       GNUNET_break (0);
   11731           0 :       rsc->status = GNUNET_SYSERR;
   11732           0 :       return;
   11733             :     }
   11734             : 
   11735           0 :     ret = rsc->cb (rsc->cb_cls,
   11736             :                    rowid,
   11737             :                    &denom_pub,
   11738             :                    ac_isnull ? NULL : &h_age_commitment,
   11739             :                    &coin_pub,
   11740             :                    &coin_sig,
   11741             :                    &amount_with_fee,
   11742             :                    noreveal_index,
   11743             :                    &rc);
   11744           0 :     GNUNET_PQ_cleanup_result (rs);
   11745           0 :     if (GNUNET_OK != ret)
   11746           0 :       break;
   11747             :   }
   11748             : }
   11749             : 
   11750             : 
   11751             : /**
   11752             :  * Select refresh sessions above @a serial_id in monotonically increasing
   11753             :  * order.
   11754             :  *
   11755             :  * @param cls closure
   11756             :  * @param serial_id highest serial ID to exclude (select strictly larger)
   11757             :  * @param cb function to call on each result
   11758             :  * @param cb_cls closure for @a cb
   11759             :  * @return transaction status code
   11760             :  */
   11761             : static enum GNUNET_DB_QueryStatus
   11762           0 : postgres_select_refreshes_above_serial_id (
   11763             :   void *cls,
   11764             :   uint64_t serial_id,
   11765             :   TALER_EXCHANGEDB_RefreshesCallback cb,
   11766             :   void *cb_cls)
   11767             : {
   11768           0 :   struct PostgresClosure *pg = cls;
   11769           0 :   struct GNUNET_PQ_QueryParam params[] = {
   11770           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   11771             :     GNUNET_PQ_query_param_end
   11772             :   };
   11773           0 :   struct RefreshsSerialContext rsc = {
   11774             :     .cb = cb,
   11775             :     .cb_cls = cb_cls,
   11776             :     .pg = pg,
   11777             :     .status = GNUNET_OK
   11778             :   };
   11779             :   enum GNUNET_DB_QueryStatus qs;
   11780             : 
   11781           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   11782             :                                              "audit_get_refresh_commitments_incr",
   11783             :                                              params,
   11784             :                                              &refreshs_serial_helper_cb,
   11785             :                                              &rsc);
   11786           0 :   if (GNUNET_OK != rsc.status)
   11787           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   11788           0 :   return qs;
   11789             : }
   11790             : 
   11791             : 
   11792             : /**
   11793             :  * Closure for #refunds_serial_helper_cb().
   11794             :  */
   11795             : struct RefundsSerialContext
   11796             : {
   11797             : 
   11798             :   /**
   11799             :    * Callback to call.
   11800             :    */
   11801             :   TALER_EXCHANGEDB_RefundCallback cb;
   11802             : 
   11803             :   /**
   11804             :    * Closure for @e cb.
   11805             :    */
   11806             :   void *cb_cls;
   11807             : 
   11808             :   /**
   11809             :    * Plugin context.
   11810             :    */
   11811             :   struct PostgresClosure *pg;
   11812             : 
   11813             :   /**
   11814             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   11815             :    */
   11816             :   enum GNUNET_GenericReturnValue status;
   11817             : };
   11818             : 
   11819             : 
   11820             : /**
   11821             :  * Helper function to be called with the results of a SELECT statement
   11822             :  * that has returned @a num_results results.
   11823             :  *
   11824             :  * @param cls closure of type `struct RefundsSerialContext`
   11825             :  * @param result the postgres result
   11826             :  * @param num_results the number of results in @a result
   11827             :  */
   11828             : static void
   11829           0 : refunds_serial_helper_cb (void *cls,
   11830             :                           PGresult *result,
   11831             :                           unsigned int num_results)
   11832             : {
   11833           0 :   struct RefundsSerialContext *rsc = cls;
   11834           0 :   struct PostgresClosure *pg = rsc->pg;
   11835             : 
   11836           0 :   for (unsigned int i = 0; i<num_results; i++)
   11837             :   {
   11838             :     struct TALER_EXCHANGEDB_Refund refund;
   11839             :     struct TALER_DenominationPublicKey denom_pub;
   11840             :     uint64_t rowid;
   11841             :     bool full_refund;
   11842           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   11843           0 :       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
   11844             :                                             &refund.details.merchant_pub),
   11845           0 :       GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
   11846             :                                             &refund.details.merchant_sig),
   11847           0 :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
   11848             :                                             &refund.details.h_contract_terms),
   11849           0 :       GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
   11850             :                                     &refund.details.rtransaction_id),
   11851           0 :       TALER_PQ_result_spec_denom_pub ("denom_pub",
   11852             :                                       &denom_pub),
   11853           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
   11854             :                                             &refund.coin.coin_pub),
   11855           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   11856             :                                    &refund.details.refund_amount),
   11857           0 :       GNUNET_PQ_result_spec_uint64 ("refund_serial_id",
   11858             :                                     &rowid),
   11859             :       GNUNET_PQ_result_spec_end
   11860             :     };
   11861             :     enum GNUNET_GenericReturnValue ret;
   11862             : 
   11863           0 :     if (GNUNET_OK !=
   11864           0 :         GNUNET_PQ_extract_result (result,
   11865             :                                   rs,
   11866             :                                   i))
   11867             :     {
   11868           0 :       GNUNET_break (0);
   11869           0 :       rsc->status = GNUNET_SYSERR;
   11870           0 :       return;
   11871             :     }
   11872             :     {
   11873           0 :       struct GNUNET_PQ_QueryParam params[] = {
   11874           0 :         GNUNET_PQ_query_param_uint64 (&rowid),
   11875             :         GNUNET_PQ_query_param_end
   11876             :       };
   11877             :       struct TALER_Amount amount_with_fee;
   11878             :       uint64_t s_f;
   11879             :       uint64_t s_v;
   11880           0 :       struct GNUNET_PQ_ResultSpec rs2[] = {
   11881           0 :         GNUNET_PQ_result_spec_uint64 ("s_v",
   11882             :                                       &s_v),
   11883           0 :         GNUNET_PQ_result_spec_uint64 ("s_f",
   11884             :                                       &s_f),
   11885           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   11886             :                                      &amount_with_fee),
   11887             :         GNUNET_PQ_result_spec_end
   11888             :       };
   11889             :       enum GNUNET_DB_QueryStatus qs;
   11890             : 
   11891           0 :       qs = GNUNET_PQ_eval_prepared_singleton_select (
   11892             :         pg->conn,
   11893             :         "test_refund_full",
   11894             :         params,
   11895             :         rs2);
   11896           0 :       if (qs <= 0)
   11897             :       {
   11898           0 :         GNUNET_break (0);
   11899           0 :         rsc->status = GNUNET_SYSERR;
   11900           0 :         return;
   11901             :       }
   11902             :       /* normalize */
   11903           0 :       s_v += s_f / TALER_AMOUNT_FRAC_BASE;
   11904           0 :       s_f %= TALER_AMOUNT_FRAC_BASE;
   11905           0 :       full_refund = (s_v >= amount_with_fee.value) &&
   11906           0 :                     (s_f >= amount_with_fee.fraction);
   11907             :     }
   11908           0 :     ret = rsc->cb (rsc->cb_cls,
   11909             :                    rowid,
   11910             :                    &denom_pub,
   11911             :                    &refund.coin.coin_pub,
   11912             :                    &refund.details.merchant_pub,
   11913             :                    &refund.details.merchant_sig,
   11914             :                    &refund.details.h_contract_terms,
   11915             :                    refund.details.rtransaction_id,
   11916             :                    full_refund,
   11917             :                    &refund.details.refund_amount);
   11918           0 :     GNUNET_PQ_cleanup_result (rs);
   11919           0 :     if (GNUNET_OK != ret)
   11920           0 :       break;
   11921             :   }
   11922             : }
   11923             : 
   11924             : 
   11925             : /**
   11926             :  * Select refunds above @a serial_id in monotonically increasing
   11927             :  * order.
   11928             :  *
   11929             :  * @param cls closure
   11930             :  * @param serial_id highest serial ID to exclude (select strictly larger)
   11931             :  * @param cb function to call on each result
   11932             :  * @param cb_cls closure for @a cb
   11933             :  * @return transaction status code
   11934             :  */
   11935             : static enum GNUNET_DB_QueryStatus
   11936           0 : postgres_select_refunds_above_serial_id (
   11937             :   void *cls,
   11938             :   uint64_t serial_id,
   11939             :   TALER_EXCHANGEDB_RefundCallback cb,
   11940             :   void *cb_cls)
   11941             : {
   11942           0 :   struct PostgresClosure *pg = cls;
   11943           0 :   struct GNUNET_PQ_QueryParam params[] = {
   11944           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   11945             :     GNUNET_PQ_query_param_end
   11946             :   };
   11947           0 :   struct RefundsSerialContext rsc = {
   11948             :     .cb = cb,
   11949             :     .cb_cls = cb_cls,
   11950             :     .pg = pg,
   11951             :     .status = GNUNET_OK
   11952             :   };
   11953             :   enum GNUNET_DB_QueryStatus qs;
   11954             : 
   11955           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   11956             :                                              "audit_get_refunds_incr",
   11957             :                                              params,
   11958             :                                              &refunds_serial_helper_cb,
   11959             :                                              &rsc);
   11960           0 :   if (GNUNET_OK != rsc.status)
   11961           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   11962           0 :   return qs;
   11963             : }
   11964             : 
   11965             : 
   11966             : /**
   11967             :  * Closure for #reserves_in_serial_helper_cb().
   11968             :  */
   11969             : struct ReservesInSerialContext
   11970             : {
   11971             : 
   11972             :   /**
   11973             :    * Callback to call.
   11974             :    */
   11975             :   TALER_EXCHANGEDB_ReserveInCallback cb;
   11976             : 
   11977             :   /**
   11978             :    * Closure for @e cb.
   11979             :    */
   11980             :   void *cb_cls;
   11981             : 
   11982             :   /**
   11983             :    * Plugin context.
   11984             :    */
   11985             :   struct PostgresClosure *pg;
   11986             : 
   11987             :   /**
   11988             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   11989             :    */
   11990             :   enum GNUNET_GenericReturnValue status;
   11991             : };
   11992             : 
   11993             : 
   11994             : /**
   11995             :  * Helper function to be called with the results of a SELECT statement
   11996             :  * that has returned @a num_results results.
   11997             :  *
   11998             :  * @param cls closure of type `struct ReservesInSerialContext`
   11999             :  * @param result the postgres result
   12000             :  * @param num_results the number of results in @a result
   12001             :  */
   12002             : static void
   12003           0 : reserves_in_serial_helper_cb (void *cls,
   12004             :                               PGresult *result,
   12005             :                               unsigned int num_results)
   12006             : {
   12007           0 :   struct ReservesInSerialContext *risc = cls;
   12008           0 :   struct PostgresClosure *pg = risc->pg;
   12009             : 
   12010           0 :   for (unsigned int i = 0; i<num_results; i++)
   12011             :   {
   12012             :     struct TALER_ReservePublicKeyP reserve_pub;
   12013             :     struct TALER_Amount credit;
   12014             :     char *sender_account_details;
   12015             :     struct GNUNET_TIME_Timestamp execution_date;
   12016             :     uint64_t rowid;
   12017             :     uint64_t wire_reference;
   12018           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   12019           0 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
   12020             :                                             &reserve_pub),
   12021           0 :       GNUNET_PQ_result_spec_uint64 ("wire_reference",
   12022             :                                     &wire_reference),
   12023           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("credit",
   12024             :                                    &credit),
   12025           0 :       GNUNET_PQ_result_spec_timestamp ("execution_date",
   12026             :                                        &execution_date),
   12027           0 :       GNUNET_PQ_result_spec_string ("sender_account_details",
   12028             :                                     &sender_account_details),
   12029           0 :       GNUNET_PQ_result_spec_uint64 ("reserve_in_serial_id",
   12030             :                                     &rowid),
   12031             :       GNUNET_PQ_result_spec_end
   12032             :     };
   12033             :     enum GNUNET_GenericReturnValue ret;
   12034             : 
   12035           0 :     if (GNUNET_OK !=
   12036           0 :         GNUNET_PQ_extract_result (result,
   12037             :                                   rs,
   12038             :                                   i))
   12039             :     {
   12040           0 :       GNUNET_break (0);
   12041           0 :       risc->status = GNUNET_SYSERR;
   12042           0 :       return;
   12043             :     }
   12044           0 :     ret = risc->cb (risc->cb_cls,
   12045             :                     rowid,
   12046             :                     &reserve_pub,
   12047             :                     &credit,
   12048             :                     sender_account_details,
   12049             :                     wire_reference,
   12050             :                     execution_date);
   12051           0 :     GNUNET_PQ_cleanup_result (rs);
   12052           0 :     if (GNUNET_OK != ret)
   12053           0 :       break;
   12054             :   }
   12055             : }
   12056             : 
   12057             : 
   12058             : /**
   12059             :  * Select inbound wire transfers into reserves_in above @a serial_id
   12060             :  * in monotonically increasing order.
   12061             :  *
   12062             :  * @param cls closure
   12063             :  * @param serial_id highest serial ID to exclude (select strictly larger)
   12064             :  * @param cb function to call on each result
   12065             :  * @param cb_cls closure for @a cb
   12066             :  * @return transaction status code
   12067             :  */
   12068             : static enum GNUNET_DB_QueryStatus
   12069           0 : postgres_select_reserves_in_above_serial_id (
   12070             :   void *cls,
   12071             :   uint64_t serial_id,
   12072             :   TALER_EXCHANGEDB_ReserveInCallback cb,
   12073             :   void *cb_cls)
   12074             : {
   12075           0 :   struct PostgresClosure *pg = cls;
   12076           0 :   struct GNUNET_PQ_QueryParam params[] = {
   12077           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   12078             :     GNUNET_PQ_query_param_end
   12079             :   };
   12080           0 :   struct ReservesInSerialContext risc = {
   12081             :     .cb = cb,
   12082             :     .cb_cls = cb_cls,
   12083             :     .pg = pg,
   12084             :     .status = GNUNET_OK
   12085             :   };
   12086             :   enum GNUNET_DB_QueryStatus qs;
   12087             : 
   12088           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   12089             :                                              "audit_reserves_in_get_transactions_incr",
   12090             :                                              params,
   12091             :                                              &reserves_in_serial_helper_cb,
   12092             :                                              &risc);
   12093           0 :   if (GNUNET_OK != risc.status)
   12094           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   12095           0 :   return qs;
   12096             : }
   12097             : 
   12098             : 
   12099             : /**
   12100             :  * Select inbound wire transfers into reserves_in above @a serial_id
   12101             :  * in monotonically increasing order by account.
   12102             :  *
   12103             :  * @param cls closure
   12104             :  * @param account_name name of the account to select by
   12105             :  * @param serial_id highest serial ID to exclude (select strictly larger)
   12106             :  * @param cb function to call on each result
   12107             :  * @param cb_cls closure for @a cb
   12108             :  * @return transaction status code
   12109             :  */
   12110             : static enum GNUNET_DB_QueryStatus
   12111           0 : postgres_select_reserves_in_above_serial_id_by_account (
   12112             :   void *cls,
   12113             :   const char *account_name,
   12114             :   uint64_t serial_id,
   12115             :   TALER_EXCHANGEDB_ReserveInCallback cb,
   12116             :   void *cb_cls)
   12117             : {
   12118           0 :   struct PostgresClosure *pg = cls;
   12119           0 :   struct GNUNET_PQ_QueryParam params[] = {
   12120           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   12121           0 :     GNUNET_PQ_query_param_string (account_name),
   12122             :     GNUNET_PQ_query_param_end
   12123             :   };
   12124           0 :   struct ReservesInSerialContext risc = {
   12125             :     .cb = cb,
   12126             :     .cb_cls = cb_cls,
   12127             :     .pg = pg,
   12128             :     .status = GNUNET_OK
   12129             :   };
   12130             :   enum GNUNET_DB_QueryStatus qs;
   12131             : 
   12132           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   12133             :                                              "audit_reserves_in_get_transactions_incr_by_account",
   12134             :                                              params,
   12135             :                                              &reserves_in_serial_helper_cb,
   12136             :                                              &risc);
   12137           0 :   if (GNUNET_OK != risc.status)
   12138           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   12139           0 :   return qs;
   12140             : }
   12141             : 
   12142             : 
   12143             : /**
   12144             :  * Closure for #reserves_out_serial_helper_cb().
   12145             :  */
   12146             : struct ReservesOutSerialContext
   12147             : {
   12148             : 
   12149             :   /**
   12150             :    * Callback to call.
   12151             :    */
   12152             :   TALER_EXCHANGEDB_WithdrawCallback cb;
   12153             : 
   12154             :   /**
   12155             :    * Closure for @e cb.
   12156             :    */
   12157             :   void *cb_cls;
   12158             : 
   12159             :   /**
   12160             :    * Plugin context.
   12161             :    */
   12162             :   struct PostgresClosure *pg;
   12163             : 
   12164             :   /**
   12165             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   12166             :    */
   12167             :   enum GNUNET_GenericReturnValue status;
   12168             : };
   12169             : 
   12170             : 
   12171             : /**
   12172             :  * Helper function to be called with the results of a SELECT statement
   12173             :  * that has returned @a num_results results.
   12174             :  *
   12175             :  * @param cls closure of type `struct ReservesOutSerialContext`
   12176             :  * @param result the postgres result
   12177             :  * @param num_results the number of results in @a result
   12178             :  */
   12179             : static void
   12180           0 : reserves_out_serial_helper_cb (void *cls,
   12181             :                                PGresult *result,
   12182             :                                unsigned int num_results)
   12183             : {
   12184           0 :   struct ReservesOutSerialContext *rosc = cls;
   12185           0 :   struct PostgresClosure *pg = rosc->pg;
   12186             : 
   12187           0 :   for (unsigned int i = 0; i<num_results; i++)
   12188             :   {
   12189             :     struct TALER_BlindedCoinHashP h_blind_ev;
   12190             :     struct TALER_DenominationPublicKey denom_pub;
   12191             :     struct TALER_ReservePublicKeyP reserve_pub;
   12192             :     struct TALER_ReserveSignatureP reserve_sig;
   12193             :     struct GNUNET_TIME_Timestamp execution_date;
   12194             :     struct TALER_Amount amount_with_fee;
   12195             :     uint64_t rowid;
   12196           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   12197           0 :       GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
   12198             :                                             &h_blind_ev),
   12199           0 :       TALER_PQ_result_spec_denom_pub ("denom_pub",
   12200             :                                       &denom_pub),
   12201           0 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
   12202             :                                             &reserve_pub),
   12203           0 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
   12204             :                                             &reserve_sig),
   12205           0 :       GNUNET_PQ_result_spec_timestamp ("execution_date",
   12206             :                                        &execution_date),
   12207           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   12208             :                                    &amount_with_fee),
   12209           0 :       GNUNET_PQ_result_spec_uint64 ("reserve_out_serial_id",
   12210             :                                     &rowid),
   12211             :       GNUNET_PQ_result_spec_end
   12212             :     };
   12213             :     enum GNUNET_GenericReturnValue ret;
   12214             : 
   12215           0 :     if (GNUNET_OK !=
   12216           0 :         GNUNET_PQ_extract_result (result,
   12217             :                                   rs,
   12218             :                                   i))
   12219             :     {
   12220           0 :       GNUNET_break (0);
   12221           0 :       rosc->status = GNUNET_SYSERR;
   12222           0 :       return;
   12223             :     }
   12224           0 :     ret = rosc->cb (rosc->cb_cls,
   12225             :                     rowid,
   12226             :                     &h_blind_ev,
   12227             :                     &denom_pub,
   12228             :                     &reserve_pub,
   12229             :                     &reserve_sig,
   12230             :                     execution_date,
   12231             :                     &amount_with_fee);
   12232           0 :     GNUNET_PQ_cleanup_result (rs);
   12233           0 :     if (GNUNET_OK != ret)
   12234           0 :       break;
   12235             :   }
   12236             : }
   12237             : 
   12238             : 
   12239             : /**
   12240             :  * Select withdraw operations from reserves_out above @a serial_id
   12241             :  * in monotonically increasing order.
   12242             :  *
   12243             :  * @param cls closure
   12244             :  * @param serial_id highest serial ID to exclude (select strictly larger)
   12245             :  * @param cb function to call on each result
   12246             :  * @param cb_cls closure for @a cb
   12247             :  * @return transaction status code
   12248             :  */
   12249             : static enum GNUNET_DB_QueryStatus
   12250           0 : postgres_select_withdrawals_above_serial_id (
   12251             :   void *cls,
   12252             :   uint64_t serial_id,
   12253             :   TALER_EXCHANGEDB_WithdrawCallback cb,
   12254             :   void *cb_cls)
   12255             : {
   12256           0 :   struct PostgresClosure *pg = cls;
   12257           0 :   struct GNUNET_PQ_QueryParam params[] = {
   12258           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   12259             :     GNUNET_PQ_query_param_end
   12260             :   };
   12261           0 :   struct ReservesOutSerialContext rosc = {
   12262             :     .cb = cb,
   12263             :     .cb_cls = cb_cls,
   12264             :     .pg = pg,
   12265             :     .status = GNUNET_OK
   12266             :   };
   12267             :   enum GNUNET_DB_QueryStatus qs;
   12268             : 
   12269           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   12270             :                                              "audit_get_reserves_out_incr",
   12271             :                                              params,
   12272             :                                              &reserves_out_serial_helper_cb,
   12273             :                                              &rosc);
   12274           0 :   if (GNUNET_OK != rosc.status)
   12275           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   12276           0 :   return qs;
   12277             : }
   12278             : 
   12279             : 
   12280             : /**
   12281             :  * Closure for #wire_out_serial_helper_cb().
   12282             :  */
   12283             : struct WireOutSerialContext
   12284             : {
   12285             : 
   12286             :   /**
   12287             :    * Callback to call.
   12288             :    */
   12289             :   TALER_EXCHANGEDB_WireTransferOutCallback cb;
   12290             : 
   12291             :   /**
   12292             :    * Closure for @e cb.
   12293             :    */
   12294             :   void *cb_cls;
   12295             : 
   12296             :   /**
   12297             :    * Plugin context.
   12298             :    */
   12299             :   struct PostgresClosure *pg;
   12300             : 
   12301             :   /**
   12302             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   12303             :    */
   12304             :   int status;
   12305             : };
   12306             : 
   12307             : 
   12308             : /**
   12309             :  * Helper function to be called with the results of a SELECT statement
   12310             :  * that has returned @a num_results results.
   12311             :  *
   12312             :  * @param cls closure of type `struct WireOutSerialContext`
   12313             :  * @param result the postgres result
   12314             :  * @param num_results the number of results in @a result
   12315             :  */
   12316             : static void
   12317           0 : wire_out_serial_helper_cb (void *cls,
   12318             :                            PGresult *result,
   12319             :                            unsigned int num_results)
   12320             : {
   12321           0 :   struct WireOutSerialContext *wosc = cls;
   12322           0 :   struct PostgresClosure *pg = wosc->pg;
   12323             : 
   12324           0 :   for (unsigned int i = 0; i<num_results; i++)
   12325             :   {
   12326             :     uint64_t rowid;
   12327             :     struct GNUNET_TIME_Timestamp date;
   12328             :     struct TALER_WireTransferIdentifierRawP wtid;
   12329             :     char *payto_uri;
   12330             :     struct TALER_Amount amount;
   12331           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   12332           0 :       GNUNET_PQ_result_spec_uint64 ("wireout_uuid",
   12333             :                                     &rowid),
   12334           0 :       GNUNET_PQ_result_spec_timestamp ("execution_date",
   12335             :                                        &date),
   12336           0 :       GNUNET_PQ_result_spec_auto_from_type ("wtid_raw",
   12337             :                                             &wtid),
   12338           0 :       GNUNET_PQ_result_spec_string ("payto_uri",
   12339             :                                     &payto_uri),
   12340           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
   12341             :                                    &amount),
   12342             :       GNUNET_PQ_result_spec_end
   12343             :     };
   12344             :     int ret;
   12345             : 
   12346           0 :     if (GNUNET_OK !=
   12347           0 :         GNUNET_PQ_extract_result (result,
   12348             :                                   rs,
   12349             :                                   i))
   12350             :     {
   12351           0 :       GNUNET_break (0);
   12352           0 :       wosc->status = GNUNET_SYSERR;
   12353           0 :       return;
   12354             :     }
   12355           0 :     ret = wosc->cb (wosc->cb_cls,
   12356             :                     rowid,
   12357             :                     date,
   12358             :                     &wtid,
   12359             :                     payto_uri,
   12360             :                     &amount);
   12361           0 :     GNUNET_PQ_cleanup_result (rs);
   12362           0 :     if (GNUNET_OK != ret)
   12363           0 :       break;
   12364             :   }
   12365             : }
   12366             : 
   12367             : 
   12368             : /**
   12369             :  * Function called to select all wire transfers the exchange
   12370             :  * executed.
   12371             :  *
   12372             :  * @param cls closure
   12373             :  * @param serial_id highest serial ID to exclude (select strictly larger)
   12374             :  * @param cb function to call for ONE unfinished item
   12375             :  * @param cb_cls closure for @a cb
   12376             :  * @return transaction status code
   12377             :  */
   12378             : static enum GNUNET_DB_QueryStatus
   12379           0 : postgres_select_wire_out_above_serial_id (
   12380             :   void *cls,
   12381             :   uint64_t serial_id,
   12382             :   TALER_EXCHANGEDB_WireTransferOutCallback cb,
   12383             :   void *cb_cls)
   12384             : {
   12385           0 :   struct PostgresClosure *pg = cls;
   12386           0 :   struct GNUNET_PQ_QueryParam params[] = {
   12387           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   12388             :     GNUNET_PQ_query_param_end
   12389             :   };
   12390           0 :   struct WireOutSerialContext wosc = {
   12391             :     .cb = cb,
   12392             :     .cb_cls = cb_cls,
   12393             :     .pg = pg,
   12394             :     .status = GNUNET_OK
   12395             :   };
   12396             :   enum GNUNET_DB_QueryStatus qs;
   12397             : 
   12398           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   12399             :                                              "audit_get_wire_incr",
   12400             :                                              params,
   12401             :                                              &wire_out_serial_helper_cb,
   12402             :                                              &wosc);
   12403           0 :   if (GNUNET_OK != wosc.status)
   12404           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   12405           0 :   return qs;
   12406             : }
   12407             : 
   12408             : 
   12409             : /**
   12410             :  * Function called to select all wire transfers the exchange
   12411             :  * executed by account.
   12412             :  *
   12413             :  * @param cls closure
   12414             :  * @param account_name account to select
   12415             :  * @param serial_id highest serial ID to exclude (select strictly larger)
   12416             :  * @param cb function to call for ONE unfinished item
   12417             :  * @param cb_cls closure for @a cb
   12418             :  * @return transaction status code
   12419             :  */
   12420             : static enum GNUNET_DB_QueryStatus
   12421           0 : postgres_select_wire_out_above_serial_id_by_account (
   12422             :   void *cls,
   12423             :   const char *account_name,
   12424             :   uint64_t serial_id,
   12425             :   TALER_EXCHANGEDB_WireTransferOutCallback cb,
   12426             :   void *cb_cls)
   12427             : {
   12428           0 :   struct PostgresClosure *pg = cls;
   12429           0 :   struct GNUNET_PQ_QueryParam params[] = {
   12430           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   12431           0 :     GNUNET_PQ_query_param_string (account_name),
   12432             :     GNUNET_PQ_query_param_end
   12433             :   };
   12434           0 :   struct WireOutSerialContext wosc = {
   12435             :     .cb = cb,
   12436             :     .cb_cls = cb_cls,
   12437             :     .pg = pg,
   12438             :     .status = GNUNET_OK
   12439             :   };
   12440             :   enum GNUNET_DB_QueryStatus qs;
   12441             : 
   12442           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   12443             :                                              "audit_get_wire_incr_by_account",
   12444             :                                              params,
   12445             :                                              &wire_out_serial_helper_cb,
   12446             :                                              &wosc);
   12447           0 :   if (GNUNET_OK != wosc.status)
   12448           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   12449           0 :   return qs;
   12450             : }
   12451             : 
   12452             : 
   12453             : /**
   12454             :  * Closure for #recoup_serial_helper_cb().
   12455             :  */
   12456             : struct RecoupSerialContext
   12457             : {
   12458             : 
   12459             :   /**
   12460             :    * Callback to call.
   12461             :    */
   12462             :   TALER_EXCHANGEDB_RecoupCallback cb;
   12463             : 
   12464             :   /**
   12465             :    * Closure for @e cb.
   12466             :    */
   12467             :   void *cb_cls;
   12468             : 
   12469             :   /**
   12470             :    * Plugin context.
   12471             :    */
   12472             :   struct PostgresClosure *pg;
   12473             : 
   12474             :   /**
   12475             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   12476             :    */
   12477             :   enum GNUNET_GenericReturnValue status;
   12478             : };
   12479             : 
   12480             : 
   12481             : /**
   12482             :  * Helper function to be called with the results of a SELECT statement
   12483             :  * that has returned @a num_results results.
   12484             :  *
   12485             :  * @param cls closure of type `struct RecoupSerialContext`
   12486             :  * @param result the postgres result
   12487             :  * @param num_results the number of results in @a result
   12488             :  */
   12489             : static void
   12490           0 : recoup_serial_helper_cb (void *cls,
   12491             :                          PGresult *result,
   12492             :                          unsigned int num_results)
   12493             : {
   12494           0 :   struct RecoupSerialContext *psc = cls;
   12495           0 :   struct PostgresClosure *pg = psc->pg;
   12496             : 
   12497           0 :   for (unsigned int i = 0; i<num_results; i++)
   12498             :   {
   12499             :     uint64_t rowid;
   12500             :     struct TALER_ReservePublicKeyP reserve_pub;
   12501             :     struct TALER_CoinPublicInfo coin;
   12502             :     struct TALER_CoinSpendSignatureP coin_sig;
   12503             :     union TALER_DenominationBlindingKeyP coin_blind;
   12504             :     struct TALER_Amount amount;
   12505             :     struct TALER_DenominationPublicKey denom_pub;
   12506             :     struct TALER_BlindedCoinHashP h_blind_ev;
   12507             :     struct GNUNET_TIME_Timestamp timestamp;
   12508           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   12509           0 :       GNUNET_PQ_result_spec_uint64 ("recoup_uuid",
   12510             :                                     &rowid),
   12511           0 :       GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
   12512             :                                        &timestamp),
   12513           0 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
   12514             :                                             &reserve_pub),
   12515           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
   12516             :                                             &coin.coin_pub),
   12517           0 :       TALER_PQ_result_spec_denom_pub ("denom_pub",
   12518             :                                       &denom_pub),
   12519           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
   12520             :                                             &coin_sig),
   12521           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
   12522             :                                             &coin_blind),
   12523           0 :       GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
   12524             :                                             &h_blind_ev),
   12525           0 :       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
   12526             :                                             &coin.denom_pub_hash),
   12527           0 :       GNUNET_PQ_result_spec_allow_null (
   12528             :         GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
   12529             :                                               &coin.h_age_commitment),
   12530             :         &coin.no_age_commitment),
   12531           0 :       TALER_PQ_result_spec_denom_sig ("denom_sig",
   12532             :                                       &coin.denom_sig),
   12533           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
   12534             :                                    &amount),
   12535             :       GNUNET_PQ_result_spec_end
   12536             :     };
   12537             :     int ret;
   12538             : 
   12539           0 :     if (GNUNET_OK !=
   12540           0 :         GNUNET_PQ_extract_result (result,
   12541             :                                   rs,
   12542             :                                   i))
   12543             :     {
   12544           0 :       GNUNET_break (0);
   12545           0 :       psc->status = GNUNET_SYSERR;
   12546           0 :       return;
   12547             :     }
   12548           0 :     ret = psc->cb (psc->cb_cls,
   12549             :                    rowid,
   12550             :                    timestamp,
   12551             :                    &amount,
   12552             :                    &reserve_pub,
   12553             :                    &coin,
   12554             :                    &denom_pub,
   12555             :                    &coin_sig,
   12556             :                    &coin_blind);
   12557           0 :     GNUNET_PQ_cleanup_result (rs);
   12558           0 :     if (GNUNET_OK != ret)
   12559           0 :       break;
   12560             :   }
   12561             : }
   12562             : 
   12563             : 
   12564             : /**
   12565             :  * Function called to select recoup requests the exchange
   12566             :  * received, ordered by serial ID (monotonically increasing).
   12567             :  *
   12568             :  * @param cls closure
   12569             :  * @param serial_id lowest serial ID to include (select larger or equal)
   12570             :  * @param cb function to call for ONE unfinished item
   12571             :  * @param cb_cls closure for @a cb
   12572             :  * @return transaction status code
   12573             :  */
   12574             : static enum GNUNET_DB_QueryStatus
   12575           0 : postgres_select_recoup_above_serial_id (
   12576             :   void *cls,
   12577             :   uint64_t serial_id,
   12578             :   TALER_EXCHANGEDB_RecoupCallback cb,
   12579             :   void *cb_cls)
   12580             : {
   12581           0 :   struct PostgresClosure *pg = cls;
   12582           0 :   struct GNUNET_PQ_QueryParam params[] = {
   12583           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   12584             :     GNUNET_PQ_query_param_end
   12585             :   };
   12586           0 :   struct RecoupSerialContext psc = {
   12587             :     .cb = cb,
   12588             :     .cb_cls = cb_cls,
   12589             :     .pg = pg,
   12590             :     .status = GNUNET_OK
   12591             :   };
   12592             :   enum GNUNET_DB_QueryStatus qs;
   12593             : 
   12594           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   12595             :                                              "recoup_get_incr",
   12596             :                                              params,
   12597             :                                              &recoup_serial_helper_cb,
   12598             :                                              &psc);
   12599           0 :   if (GNUNET_OK != psc.status)
   12600           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   12601           0 :   return qs;
   12602             : }
   12603             : 
   12604             : 
   12605             : /**
   12606             :  * Closure for #recoup_refresh_serial_helper_cb().
   12607             :  */
   12608             : struct RecoupRefreshSerialContext
   12609             : {
   12610             : 
   12611             :   /**
   12612             :    * Callback to call.
   12613             :    */
   12614             :   TALER_EXCHANGEDB_RecoupRefreshCallback cb;
   12615             : 
   12616             :   /**
   12617             :    * Closure for @e cb.
   12618             :    */
   12619             :   void *cb_cls;
   12620             : 
   12621             :   /**
   12622             :    * Plugin context.
   12623             :    */
   12624             :   struct PostgresClosure *pg;
   12625             : 
   12626             :   /**
   12627             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   12628             :    */
   12629             :   enum GNUNET_GenericReturnValue status;
   12630             : };
   12631             : 
   12632             : 
   12633             : /**
   12634             :  * Helper function to be called with the results of a SELECT statement
   12635             :  * that has returned @a num_results results.
   12636             :  *
   12637             :  * @param cls closure of type `struct RecoupRefreshSerialContext`
   12638             :  * @param result the postgres result
   12639             :  * @param num_results the number of results in @a result
   12640             :  */
   12641             : static void
   12642           0 : recoup_refresh_serial_helper_cb (void *cls,
   12643             :                                  PGresult *result,
   12644             :                                  unsigned int num_results)
   12645             : {
   12646           0 :   struct RecoupRefreshSerialContext *psc = cls;
   12647           0 :   struct PostgresClosure *pg = psc->pg;
   12648             : 
   12649           0 :   for (unsigned int i = 0; i<num_results; i++)
   12650             :   {
   12651             :     uint64_t rowid;
   12652             :     struct TALER_CoinSpendPublicKeyP old_coin_pub;
   12653             :     struct TALER_CoinPublicInfo coin;
   12654             :     struct TALER_CoinSpendSignatureP coin_sig;
   12655             :     union TALER_DenominationBlindingKeyP coin_blind;
   12656             :     struct TALER_DenominationPublicKey denom_pub;
   12657             :     struct TALER_DenominationHashP old_denom_pub_hash;
   12658             :     struct TALER_Amount amount;
   12659             :     struct TALER_BlindedCoinHashP h_blind_ev;
   12660             :     struct GNUNET_TIME_Timestamp timestamp;
   12661           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   12662           0 :       GNUNET_PQ_result_spec_uint64 ("recoup_refresh_uuid",
   12663             :                                     &rowid),
   12664           0 :       GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
   12665             :                                        &timestamp),
   12666           0 :       GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
   12667             :                                             &old_coin_pub),
   12668           0 :       GNUNET_PQ_result_spec_auto_from_type ("old_denom_pub_hash",
   12669             :                                             &old_denom_pub_hash),
   12670           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
   12671             :                                             &coin.coin_pub),
   12672           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
   12673             :                                             &coin_sig),
   12674           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
   12675             :                                             &coin_blind),
   12676           0 :       TALER_PQ_result_spec_denom_pub ("denom_pub",
   12677             :                                       &denom_pub),
   12678           0 :       GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
   12679             :                                             &h_blind_ev),
   12680           0 :       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
   12681             :                                             &coin.denom_pub_hash),
   12682           0 :       GNUNET_PQ_result_spec_allow_null (
   12683             :         GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
   12684             :                                               &coin.h_age_commitment),
   12685             :         &coin.no_age_commitment),
   12686           0 :       TALER_PQ_result_spec_denom_sig ("denom_sig",
   12687             :                                       &coin.denom_sig),
   12688           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
   12689             :                                    &amount),
   12690             :       GNUNET_PQ_result_spec_end
   12691             :     };
   12692             :     enum GNUNET_GenericReturnValue ret;
   12693             : 
   12694           0 :     if (GNUNET_OK !=
   12695           0 :         GNUNET_PQ_extract_result (result,
   12696             :                                   rs,
   12697             :                                   i))
   12698             :     {
   12699           0 :       GNUNET_break (0);
   12700           0 :       psc->status = GNUNET_SYSERR;
   12701           0 :       return;
   12702             :     }
   12703           0 :     ret = psc->cb (psc->cb_cls,
   12704             :                    rowid,
   12705             :                    timestamp,
   12706             :                    &amount,
   12707             :                    &old_coin_pub,
   12708             :                    &old_denom_pub_hash,
   12709             :                    &coin,
   12710             :                    &denom_pub,
   12711             :                    &coin_sig,
   12712             :                    &coin_blind);
   12713           0 :     GNUNET_PQ_cleanup_result (rs);
   12714           0 :     if (GNUNET_OK != ret)
   12715           0 :       break;
   12716             :   }
   12717             : }
   12718             : 
   12719             : 
   12720             : /**
   12721             :  * Function called to select recoup requests the exchange received for
   12722             :  * refreshed coins, ordered by serial ID (monotonically increasing).
   12723             :  *
   12724             :  * @param cls closure
   12725             :  * @param serial_id lowest serial ID to include (select larger or equal)
   12726             :  * @param cb function to call for ONE unfinished item
   12727             :  * @param cb_cls closure for @a cb
   12728             :  * @return transaction status code
   12729             :  */
   12730             : static enum GNUNET_DB_QueryStatus
   12731           0 : postgres_select_recoup_refresh_above_serial_id (
   12732             :   void *cls,
   12733             :   uint64_t serial_id,
   12734             :   TALER_EXCHANGEDB_RecoupRefreshCallback cb,
   12735             :   void *cb_cls)
   12736             : {
   12737           0 :   struct PostgresClosure *pg = cls;
   12738           0 :   struct GNUNET_PQ_QueryParam params[] = {
   12739           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   12740             :     GNUNET_PQ_query_param_end
   12741             :   };
   12742           0 :   struct RecoupRefreshSerialContext psc = {
   12743             :     .cb = cb,
   12744             :     .cb_cls = cb_cls,
   12745             :     .pg = pg,
   12746             :     .status = GNUNET_OK
   12747             :   };
   12748             :   enum GNUNET_DB_QueryStatus qs;
   12749             : 
   12750           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   12751             :                                              "recoup_refresh_get_incr",
   12752             :                                              params,
   12753             :                                              &recoup_refresh_serial_helper_cb,
   12754             :                                              &psc);
   12755           0 :   if (GNUNET_OK != psc.status)
   12756           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   12757           0 :   return qs;
   12758             : }
   12759             : 
   12760             : 
   12761             : /**
   12762             :  * Closure for #reserve_closed_serial_helper_cb().
   12763             :  */
   12764             : struct ReserveClosedSerialContext
   12765             : {
   12766             : 
   12767             :   /**
   12768             :    * Callback to call.
   12769             :    */
   12770             :   TALER_EXCHANGEDB_ReserveClosedCallback cb;
   12771             : 
   12772             :   /**
   12773             :    * Closure for @e cb.
   12774             :    */
   12775             :   void *cb_cls;
   12776             : 
   12777             :   /**
   12778             :    * Plugin's context.
   12779             :    */
   12780             :   struct PostgresClosure *pg;
   12781             : 
   12782             :   /**
   12783             :    * Status code, set to #GNUNET_SYSERR on hard errors.
   12784             :    */
   12785             :   enum GNUNET_GenericReturnValue status;
   12786             : };
   12787             : 
   12788             : 
   12789             : /**
   12790             :  * Helper function to be called with the results of a SELECT statement
   12791             :  * that has returned @a num_results results.
   12792             :  *
   12793             :  * @param cls closure of type `struct ReserveClosedSerialContext`
   12794             :  * @param result the postgres result
   12795             :  * @param num_results the number of results in @a result
   12796             :  */
   12797             : static void
   12798           0 : reserve_closed_serial_helper_cb (void *cls,
   12799             :                                  PGresult *result,
   12800             :                                  unsigned int num_results)
   12801             : {
   12802           0 :   struct ReserveClosedSerialContext *rcsc = cls;
   12803           0 :   struct PostgresClosure *pg = rcsc->pg;
   12804             : 
   12805           0 :   for (unsigned int i = 0; i<num_results; i++)
   12806             :   {
   12807             :     uint64_t rowid;
   12808             :     struct TALER_ReservePublicKeyP reserve_pub;
   12809             :     char *receiver_account;
   12810             :     struct TALER_WireTransferIdentifierRawP wtid;
   12811             :     struct TALER_Amount amount_with_fee;
   12812             :     struct TALER_Amount closing_fee;
   12813             :     struct GNUNET_TIME_Timestamp execution_date;
   12814           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   12815           0 :       GNUNET_PQ_result_spec_uint64 ("close_uuid",
   12816             :                                     &rowid),
   12817           0 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
   12818             :                                             &reserve_pub),
   12819           0 :       GNUNET_PQ_result_spec_timestamp ("execution_date",
   12820             :                                        &execution_date),
   12821           0 :       GNUNET_PQ_result_spec_auto_from_type ("wtid",
   12822             :                                             &wtid),
   12823           0 :       GNUNET_PQ_result_spec_string ("receiver_account",
   12824             :                                     &receiver_account),
   12825           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
   12826             :                                    &amount_with_fee),
   12827           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
   12828             :                                    &closing_fee),
   12829             :       GNUNET_PQ_result_spec_end
   12830             :     };
   12831             :     enum GNUNET_GenericReturnValue ret;
   12832             : 
   12833           0 :     if (GNUNET_OK !=
   12834           0 :         GNUNET_PQ_extract_result (result,
   12835             :                                   rs,
   12836             :                                   i))
   12837             :     {
   12838           0 :       GNUNET_break (0);
   12839           0 :       rcsc->status = GNUNET_SYSERR;
   12840           0 :       return;
   12841             :     }
   12842           0 :     ret = rcsc->cb (rcsc->cb_cls,
   12843             :                     rowid,
   12844             :                     execution_date,
   12845             :                     &amount_with_fee,
   12846             :                     &closing_fee,
   12847             :                     &reserve_pub,
   12848             :                     receiver_account,
   12849             :                     &wtid);
   12850           0 :     GNUNET_PQ_cleanup_result (rs);
   12851           0 :     if (GNUNET_OK != ret)
   12852           0 :       break;
   12853             :   }
   12854             : }
   12855             : 
   12856             : 
   12857             : /**
   12858             :  * Function called to select reserve close operations the aggregator
   12859             :  * triggered, ordered by serial ID (monotonically increasing).
   12860             :  *
   12861             :  * @param cls closure
   12862             :  * @param serial_id lowest serial ID to include (select larger or equal)
   12863             :  * @param cb function to call for ONE unfinished item
   12864             :  * @param cb_cls closure for @a cb
   12865             :  * @return transaction status code
   12866             :  */
   12867             : static enum GNUNET_DB_QueryStatus
   12868           0 : postgres_select_reserve_closed_above_serial_id (
   12869             :   void *cls,
   12870             :   uint64_t serial_id,
   12871             :   TALER_EXCHANGEDB_ReserveClosedCallback cb,
   12872             :   void *cb_cls)
   12873             : {
   12874           0 :   struct PostgresClosure *pg = cls;
   12875           0 :   struct GNUNET_PQ_QueryParam params[] = {
   12876           0 :     GNUNET_PQ_query_param_uint64 (&serial_id),
   12877             :     GNUNET_PQ_query_param_end
   12878             :   };
   12879           0 :   struct ReserveClosedSerialContext rcsc = {
   12880             :     .cb = cb,
   12881             :     .cb_cls = cb_cls,
   12882             :     .pg = pg,
   12883             :     .status = GNUNET_OK
   12884             :   };
   12885             :   enum GNUNET_DB_QueryStatus qs;
   12886             : 
   12887           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   12888             :                                              "reserves_close_get_incr",
   12889             :                                              params,
   12890             :                                              &reserve_closed_serial_helper_cb,
   12891             :                                              &rcsc);
   12892           0 :   if (GNUNET_OK != rcsc.status)
   12893           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   12894           0 :   return qs;
   12895             : }
   12896             : 
   12897             : 
   12898             : /**
   12899             :  * Obtain information about which reserve a coin was generated
   12900             :  * from given the hash of the blinded coin.
   12901             :  *
   12902             :  * @param cls closure
   12903             :  * @param bch hash that uniquely identifies the withdraw request
   12904             :  * @param[out] reserve_pub set to information about the reserve (on success only)
   12905             :  * @param[out] reserve_out_serial_id set to row of the @a h_blind_ev in reserves_out
   12906             :  * @return transaction status code
   12907             :  */
   12908             : static enum GNUNET_DB_QueryStatus
   12909           0 : postgres_get_reserve_by_h_blind (
   12910             :   void *cls,
   12911             :   const struct TALER_BlindedCoinHashP *bch,
   12912             :   struct TALER_ReservePublicKeyP *reserve_pub,
   12913             :   uint64_t *reserve_out_serial_id)
   12914             : {
   12915           0 :   struct PostgresClosure *pg = cls;
   12916           0 :   struct GNUNET_PQ_QueryParam params[] = {
   12917           0 :     GNUNET_PQ_query_param_auto_from_type (bch),
   12918             :     GNUNET_PQ_query_param_end
   12919             :   };
   12920           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   12921           0 :     GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
   12922             :                                           reserve_pub),
   12923           0 :     GNUNET_PQ_result_spec_uint64 ("reserve_out_serial_id",
   12924             :                                   reserve_out_serial_id),
   12925             :     GNUNET_PQ_result_spec_end
   12926             :   };
   12927             : 
   12928           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   12929             :                                                    "reserve_by_h_blind",
   12930             :                                                    params,
   12931             :                                                    rs);
   12932             : }
   12933             : 
   12934             : 
   12935             : /**
   12936             :  * Obtain information about which old coin a coin was refreshed
   12937             :  * given the hash of the blinded (fresh) coin.
   12938             :  *
   12939             :  * @param cls closure
   12940             :  * @param h_blind_ev hash of the blinded coin
   12941             :  * @param[out] old_coin_pub set to information about the old coin (on success only)
   12942             :  * @param[out] rrc_serial set to serial number of the entry in the database
   12943             :  * @return transaction status code
   12944             :  */
   12945             : static enum GNUNET_DB_QueryStatus
   12946           0 : postgres_get_old_coin_by_h_blind (
   12947             :   void *cls,
   12948             :   const struct TALER_BlindedCoinHashP *h_blind_ev,
   12949             :   struct TALER_CoinSpendPublicKeyP *old_coin_pub,
   12950             :   uint64_t *rrc_serial)
   12951             : {
   12952           0 :   struct PostgresClosure *pg = cls;
   12953           0 :   struct GNUNET_PQ_QueryParam params[] = {
   12954           0 :     GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
   12955             :     GNUNET_PQ_query_param_end
   12956             :   };
   12957           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   12958           0 :     GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
   12959             :                                           old_coin_pub),
   12960           0 :     GNUNET_PQ_result_spec_uint64 ("rrc_serial",
   12961             :                                   rrc_serial),
   12962             :     GNUNET_PQ_result_spec_end
   12963             :   };
   12964             : 
   12965           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   12966             :                                                    "old_coin_by_h_blind",
   12967             :                                                    params,
   12968             :                                                    rs);
   12969             : }
   12970             : 
   12971             : 
   12972             : /**
   12973             :  * Store information that a denomination key was revoked
   12974             :  * in the database.
   12975             :  *
   12976             :  * @param cls closure
   12977             :  * @param denom_pub_hash hash of the revoked denomination key
   12978             :  * @param master_sig signature affirming the revocation
   12979             :  * @return transaction status code
   12980             :  */
   12981             : static enum GNUNET_DB_QueryStatus
   12982           0 : postgres_insert_denomination_revocation (
   12983             :   void *cls,
   12984             :   const struct TALER_DenominationHashP *denom_pub_hash,
   12985             :   const struct TALER_MasterSignatureP *master_sig)
   12986             : {
   12987           0 :   struct PostgresClosure *pg = cls;
   12988           0 :   struct GNUNET_PQ_QueryParam params[] = {
   12989           0 :     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
   12990           0 :     GNUNET_PQ_query_param_auto_from_type (master_sig),
   12991             :     GNUNET_PQ_query_param_end
   12992             :   };
   12993             : 
   12994           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   12995             :                                              "denomination_revocation_insert",
   12996             :                                              params);
   12997             : }
   12998             : 
   12999             : 
   13000             : /**
   13001             :  * Obtain information about a denomination key's revocation from
   13002             :  * the database.
   13003             :  *
   13004             :  * @param cls closure
   13005             :  * @param denom_pub_hash hash of the revoked denomination key
   13006             :  * @param[out] master_sig signature affirming the revocation
   13007             :  * @param[out] rowid row where the information is stored
   13008             :  * @return transaction status code
   13009             :  */
   13010             : static enum GNUNET_DB_QueryStatus
   13011           0 : postgres_get_denomination_revocation (
   13012             :   void *cls,
   13013             :   const struct TALER_DenominationHashP *denom_pub_hash,
   13014             :   struct TALER_MasterSignatureP *master_sig,
   13015             :   uint64_t *rowid)
   13016             : {
   13017           0 :   struct PostgresClosure *pg = cls;
   13018           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13019           0 :     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
   13020             :     GNUNET_PQ_query_param_end
   13021             :   };
   13022           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   13023           0 :     GNUNET_PQ_result_spec_auto_from_type ("master_sig",
   13024             :                                           master_sig),
   13025           0 :     GNUNET_PQ_result_spec_uint64 ("denom_revocations_serial_id",
   13026             :                                   rowid),
   13027             :     GNUNET_PQ_result_spec_end
   13028             :   };
   13029             : 
   13030           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   13031             :                                                    "denomination_revocation_get",
   13032             :                                                    params,
   13033             :                                                    rs);
   13034             : }
   13035             : 
   13036             : 
   13037             : /**
   13038             :  * Closure for #missing_wire_cb().
   13039             :  */
   13040             : struct MissingWireContext
   13041             : {
   13042             :   /**
   13043             :    * Function to call per result.
   13044             :    */
   13045             :   TALER_EXCHANGEDB_WireMissingCallback cb;
   13046             : 
   13047             :   /**
   13048             :    * Closure for @e cb.
   13049             :    */
   13050             :   void *cb_cls;
   13051             : 
   13052             :   /**
   13053             :    * Plugin context.
   13054             :    */
   13055             :   struct PostgresClosure *pg;
   13056             : 
   13057             :   /**
   13058             :    * Set to #GNUNET_SYSERR on error.
   13059             :    */
   13060             :   enum GNUNET_GenericReturnValue status;
   13061             : };
   13062             : 
   13063             : 
   13064             : /**
   13065             :  * Invoke the callback for each result.
   13066             :  *
   13067             :  * @param cls a `struct MissingWireContext *`
   13068             :  * @param result SQL result
   13069             :  * @param num_results number of rows in @a result
   13070             :  */
   13071             : static void
   13072           0 : missing_wire_cb (void *cls,
   13073             :                  PGresult *result,
   13074             :                  unsigned int num_results)
   13075             : {
   13076           0 :   struct MissingWireContext *mwc = cls;
   13077           0 :   struct PostgresClosure *pg = mwc->pg;
   13078             : 
   13079           0 :   while (0 < num_results)
   13080             :   {
   13081             :     uint64_t rowid;
   13082             :     struct TALER_CoinSpendPublicKeyP coin_pub;
   13083             :     struct TALER_Amount amount;
   13084             :     char *payto_uri;
   13085             :     struct GNUNET_TIME_Timestamp deadline;
   13086             :     bool done;
   13087           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   13088           0 :       GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
   13089             :                                     &rowid),
   13090           0 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
   13091             :                                             &coin_pub),
   13092           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   13093             :                                    &amount),
   13094           0 :       GNUNET_PQ_result_spec_string ("payto_uri",
   13095             :                                     &payto_uri),
   13096           0 :       GNUNET_PQ_result_spec_timestamp ("wire_deadline",
   13097             :                                        &deadline),
   13098           0 :       GNUNET_PQ_result_spec_bool ("done",
   13099             :                                   &done),
   13100             :       GNUNET_PQ_result_spec_end
   13101             :     };
   13102             : 
   13103           0 :     if (GNUNET_OK !=
   13104           0 :         GNUNET_PQ_extract_result (result,
   13105             :                                   rs,
   13106             :                                   --num_results))
   13107             :     {
   13108           0 :       GNUNET_break (0);
   13109           0 :       mwc->status = GNUNET_SYSERR;
   13110           0 :       return;
   13111             :     }
   13112           0 :     mwc->cb (mwc->cb_cls,
   13113             :              rowid,
   13114             :              &coin_pub,
   13115             :              &amount,
   13116             :              payto_uri,
   13117             :              deadline,
   13118             :              done);
   13119           0 :     GNUNET_PQ_cleanup_result (rs);
   13120             :   }
   13121             : }
   13122             : 
   13123             : 
   13124             : /**
   13125             :  * Select all of those deposits in the database for which we do
   13126             :  * not have a wire transfer (or a refund) and which should have
   13127             :  * been deposited between @a start_date and @a end_date.
   13128             :  *
   13129             :  * @param cls closure
   13130             :  * @param start_date lower bound on the requested wire execution date
   13131             :  * @param end_date upper bound on the requested wire execution date
   13132             :  * @param cb function to call on all such deposits
   13133             :  * @param cb_cls closure for @a cb
   13134             :  * @return transaction status code
   13135             :  */
   13136             : static enum GNUNET_DB_QueryStatus
   13137           0 : postgres_select_deposits_missing_wire (void *cls,
   13138             :                                        struct GNUNET_TIME_Timestamp start_date,
   13139             :                                        struct GNUNET_TIME_Timestamp end_date,
   13140             :                                        TALER_EXCHANGEDB_WireMissingCallback cb,
   13141             :                                        void *cb_cls)
   13142             : {
   13143           0 :   struct PostgresClosure *pg = cls;
   13144           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13145           0 :     GNUNET_PQ_query_param_timestamp (&start_date),
   13146           0 :     GNUNET_PQ_query_param_timestamp (&end_date),
   13147             :     GNUNET_PQ_query_param_end
   13148             :   };
   13149           0 :   struct MissingWireContext mwc = {
   13150             :     .cb = cb,
   13151             :     .cb_cls = cb_cls,
   13152             :     .pg = pg,
   13153             :     .status = GNUNET_OK
   13154             :   };
   13155             :   enum GNUNET_DB_QueryStatus qs;
   13156             : 
   13157           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   13158             :                                              "deposits_get_overdue",
   13159             :                                              params,
   13160             :                                              &missing_wire_cb,
   13161             :                                              &mwc);
   13162           0 :   if (GNUNET_OK != mwc.status)
   13163           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   13164           0 :   return qs;
   13165             : }
   13166             : 
   13167             : 
   13168             : /**
   13169             :  * Check the last date an auditor was modified.
   13170             :  *
   13171             :  * @param cls closure
   13172             :  * @param auditor_pub key to look up information for
   13173             :  * @param[out] last_date last modification date to auditor status
   13174             :  * @return transaction status code
   13175             :  */
   13176             : static enum GNUNET_DB_QueryStatus
   13177           0 : postgres_lookup_auditor_timestamp (
   13178             :   void *cls,
   13179             :   const struct TALER_AuditorPublicKeyP *auditor_pub,
   13180             :   struct GNUNET_TIME_Timestamp *last_date)
   13181             : {
   13182           0 :   struct PostgresClosure *pg = cls;
   13183           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13184           0 :     GNUNET_PQ_query_param_auto_from_type (auditor_pub),
   13185             :     GNUNET_PQ_query_param_end
   13186             :   };
   13187           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   13188           0 :     GNUNET_PQ_result_spec_timestamp ("last_change",
   13189             :                                      last_date),
   13190             :     GNUNET_PQ_result_spec_end
   13191             :   };
   13192             : 
   13193           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   13194             :                                                    "lookup_auditor_timestamp",
   13195             :                                                    params,
   13196             :                                                    rs);
   13197             : }
   13198             : 
   13199             : 
   13200             : /**
   13201             :  * Lookup current state of an auditor.
   13202             :  *
   13203             :  * @param cls closure
   13204             :  * @param auditor_pub key to look up information for
   13205             :  * @param[out] auditor_url set to the base URL of the auditor's REST API; memory to be
   13206             :  *            released by the caller!
   13207             :  * @param[out] enabled set if the auditor is currently in use
   13208             :  * @return transaction status code
   13209             :  */
   13210             : static enum GNUNET_DB_QueryStatus
   13211           0 : postgres_lookup_auditor_status (
   13212             :   void *cls,
   13213             :   const struct TALER_AuditorPublicKeyP *auditor_pub,
   13214             :   char **auditor_url,
   13215             :   bool *enabled)
   13216             : {
   13217           0 :   struct PostgresClosure *pg = cls;
   13218           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13219           0 :     GNUNET_PQ_query_param_auto_from_type (auditor_pub),
   13220             :     GNUNET_PQ_query_param_end
   13221             :   };
   13222           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   13223           0 :     GNUNET_PQ_result_spec_string ("auditor_url",
   13224             :                                   auditor_url),
   13225           0 :     GNUNET_PQ_result_spec_bool ("is_active",
   13226             :                                 enabled),
   13227             :     GNUNET_PQ_result_spec_end
   13228             :   };
   13229             : 
   13230           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   13231             :                                                    "lookup_auditor_status",
   13232             :                                                    params,
   13233             :                                                    rs);
   13234             : }
   13235             : 
   13236             : 
   13237             : /**
   13238             :  * Insert information about an auditor that will audit this exchange.
   13239             :  *
   13240             :  * @param cls closure
   13241             :  * @param auditor_pub key of the auditor
   13242             :  * @param auditor_url base URL of the auditor's REST service
   13243             :  * @param auditor_name name of the auditor (for humans)
   13244             :  * @param start_date date when the auditor was added by the offline system
   13245             :  *                      (only to be used for replay detection)
   13246             :  * @return transaction status code
   13247             :  */
   13248             : static enum GNUNET_DB_QueryStatus
   13249           0 : postgres_insert_auditor (void *cls,
   13250             :                          const struct TALER_AuditorPublicKeyP *auditor_pub,
   13251             :                          const char *auditor_url,
   13252             :                          const char *auditor_name,
   13253             :                          struct GNUNET_TIME_Timestamp start_date)
   13254             : {
   13255           0 :   struct PostgresClosure *pg = cls;
   13256           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13257           0 :     GNUNET_PQ_query_param_auto_from_type (auditor_pub),
   13258           0 :     GNUNET_PQ_query_param_string (auditor_name),
   13259           0 :     GNUNET_PQ_query_param_string (auditor_url),
   13260           0 :     GNUNET_PQ_query_param_timestamp (&start_date),
   13261             :     GNUNET_PQ_query_param_end
   13262             :   };
   13263             : 
   13264           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   13265             :                                              "insert_auditor",
   13266             :                                              params);
   13267             : }
   13268             : 
   13269             : 
   13270             : /**
   13271             :  * Update information about an auditor that will audit this exchange.
   13272             :  *
   13273             :  * @param cls closure
   13274             :  * @param auditor_pub key of the auditor (primary key for the existing record)
   13275             :  * @param auditor_url base URL of the auditor's REST service, to be updated
   13276             :  * @param auditor_name name of the auditor (for humans)
   13277             :  * @param change_date date when the auditor status was last changed
   13278             :  *                      (only to be used for replay detection)
   13279             :  * @param enabled true to enable, false to disable
   13280             :  * @return transaction status code
   13281             :  */
   13282             : static enum GNUNET_DB_QueryStatus
   13283           0 : postgres_update_auditor (void *cls,
   13284             :                          const struct TALER_AuditorPublicKeyP *auditor_pub,
   13285             :                          const char *auditor_url,
   13286             :                          const char *auditor_name,
   13287             :                          struct GNUNET_TIME_Timestamp change_date,
   13288             :                          bool enabled)
   13289             : {
   13290           0 :   struct PostgresClosure *pg = cls;
   13291           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13292           0 :     GNUNET_PQ_query_param_auto_from_type (auditor_pub),
   13293           0 :     GNUNET_PQ_query_param_string (auditor_url),
   13294           0 :     GNUNET_PQ_query_param_string (auditor_name),
   13295           0 :     GNUNET_PQ_query_param_bool (enabled),
   13296           0 :     GNUNET_PQ_query_param_timestamp (&change_date),
   13297             :     GNUNET_PQ_query_param_end
   13298             :   };
   13299             : 
   13300           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   13301             :                                              "update_auditor",
   13302             :                                              params);
   13303             : }
   13304             : 
   13305             : 
   13306             : /**
   13307             :  * Check the last date an exchange wire account was modified.
   13308             :  *
   13309             :  * @param cls closure
   13310             :  * @param payto_uri key to look up information for
   13311             :  * @param[out] last_date last modification date to auditor status
   13312             :  * @return transaction status code
   13313             :  */
   13314             : static enum GNUNET_DB_QueryStatus
   13315           0 : postgres_lookup_wire_timestamp (void *cls,
   13316             :                                 const char *payto_uri,
   13317             :                                 struct GNUNET_TIME_Timestamp *last_date)
   13318             : {
   13319           0 :   struct PostgresClosure *pg = cls;
   13320           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13321           0 :     GNUNET_PQ_query_param_string (payto_uri),
   13322             :     GNUNET_PQ_query_param_end
   13323             :   };
   13324           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   13325           0 :     GNUNET_PQ_result_spec_timestamp ("last_change",
   13326             :                                      last_date),
   13327             :     GNUNET_PQ_result_spec_end
   13328             :   };
   13329             : 
   13330           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   13331             :                                                    "lookup_wire_timestamp",
   13332             :                                                    params,
   13333             :                                                    rs);
   13334             : }
   13335             : 
   13336             : 
   13337             : /**
   13338             :  * Insert information about an wire account used by this exchange.
   13339             :  *
   13340             :  * @param cls closure
   13341             :  * @param payto_uri wire account of the exchange
   13342             :  * @param start_date date when the account was added by the offline system
   13343             :  *                      (only to be used for replay detection)
   13344             :  * @param master_sig public signature affirming the existence of the account,
   13345             :  *         must be of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS
   13346             :  * @return transaction status code
   13347             :  */
   13348             : static enum GNUNET_DB_QueryStatus
   13349           0 : postgres_insert_wire (void *cls,
   13350             :                       const char *payto_uri,
   13351             :                       struct GNUNET_TIME_Timestamp start_date,
   13352             :                       const struct TALER_MasterSignatureP *master_sig)
   13353             : {
   13354           0 :   struct PostgresClosure *pg = cls;
   13355           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13356           0 :     GNUNET_PQ_query_param_string (payto_uri),
   13357           0 :     GNUNET_PQ_query_param_auto_from_type (master_sig),
   13358           0 :     GNUNET_PQ_query_param_timestamp (&start_date),
   13359             :     GNUNET_PQ_query_param_end
   13360             :   };
   13361             : 
   13362           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   13363             :                                              "insert_wire",
   13364             :                                              params);
   13365             : }
   13366             : 
   13367             : 
   13368             : /**
   13369             :  * Update information about a wire account of the exchange.
   13370             :  *
   13371             :  * @param cls closure
   13372             :  * @param payto_uri account the update is about
   13373             :  * @param change_date date when the account status was last changed
   13374             :  *                      (only to be used for replay detection)
   13375             :  * @param enabled true to enable, false to disable (the actual change)
   13376             :  * @return transaction status code
   13377             :  */
   13378             : static enum GNUNET_DB_QueryStatus
   13379           0 : postgres_update_wire (void *cls,
   13380             :                       const char *payto_uri,
   13381             :                       struct GNUNET_TIME_Timestamp change_date,
   13382             :                       bool enabled)
   13383             : {
   13384           0 :   struct PostgresClosure *pg = cls;
   13385           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13386           0 :     GNUNET_PQ_query_param_string (payto_uri),
   13387           0 :     GNUNET_PQ_query_param_bool (enabled),
   13388           0 :     GNUNET_PQ_query_param_timestamp (&change_date),
   13389             :     GNUNET_PQ_query_param_end
   13390             :   };
   13391             : 
   13392           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   13393             :                                              "update_wire",
   13394             :                                              params);
   13395             : }
   13396             : 
   13397             : 
   13398             : /**
   13399             :  * Closure for #get_wire_accounts_cb().
   13400             :  */
   13401             : struct GetWireAccountsContext
   13402             : {
   13403             :   /**
   13404             :    * Function to call per result.
   13405             :    */
   13406             :   TALER_EXCHANGEDB_WireAccountCallback cb;
   13407             : 
   13408             :   /**
   13409             :    * Closure for @e cb.
   13410             :    */
   13411             :   void *cb_cls;
   13412             : 
   13413             :   /**
   13414             :    * Flag set to #GNUNET_OK as long as everything is fine.
   13415             :    */
   13416             :   enum GNUNET_GenericReturnValue status;
   13417             : 
   13418             : };
   13419             : 
   13420             : 
   13421             : /**
   13422             :  * Invoke the callback for each result.
   13423             :  *
   13424             :  * @param cls a `struct MissingWireContext *`
   13425             :  * @param result SQL result
   13426             :  * @param num_results number of rows in @a result
   13427             :  */
   13428             : static void
   13429           0 : get_wire_accounts_cb (void *cls,
   13430             :                       PGresult *result,
   13431             :                       unsigned int num_results)
   13432             : {
   13433           0 :   struct GetWireAccountsContext *ctx = cls;
   13434             : 
   13435           0 :   for (unsigned int i = 0; i < num_results; i++)
   13436             :   {
   13437             :     char *payto_uri;
   13438             :     struct TALER_MasterSignatureP master_sig;
   13439           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   13440           0 :       GNUNET_PQ_result_spec_string ("payto_uri",
   13441             :                                     &payto_uri),
   13442           0 :       GNUNET_PQ_result_spec_auto_from_type ("master_sig",
   13443             :                                             &master_sig),
   13444             :       GNUNET_PQ_result_spec_end
   13445             :     };
   13446             : 
   13447           0 :     if (GNUNET_OK !=
   13448           0 :         GNUNET_PQ_extract_result (result,
   13449             :                                   rs,
   13450             :                                   i))
   13451             :     {
   13452           0 :       GNUNET_break (0);
   13453           0 :       ctx->status = GNUNET_SYSERR;
   13454           0 :       return;
   13455             :     }
   13456           0 :     ctx->cb (ctx->cb_cls,
   13457             :              payto_uri,
   13458             :              &master_sig);
   13459           0 :     GNUNET_PQ_cleanup_result (rs);
   13460             :   }
   13461             : }
   13462             : 
   13463             : 
   13464             : /**
   13465             :  * Obtain information about the enabled wire accounts of the exchange.
   13466             :  *
   13467             :  * @param cls closure
   13468             :  * @param cb function to call on each account
   13469             :  * @param cb_cls closure for @a cb
   13470             :  * @return transaction status code
   13471             :  */
   13472             : static enum GNUNET_DB_QueryStatus
   13473           0 : postgres_get_wire_accounts (void *cls,
   13474             :                             TALER_EXCHANGEDB_WireAccountCallback cb,
   13475             :                             void *cb_cls)
   13476             : {
   13477           0 :   struct PostgresClosure *pg = cls;
   13478           0 :   struct GetWireAccountsContext ctx = {
   13479             :     .cb = cb,
   13480             :     .cb_cls = cb_cls,
   13481             :     .status = GNUNET_OK
   13482             :   };
   13483           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13484             :     GNUNET_PQ_query_param_end
   13485             :   };
   13486             :   enum GNUNET_DB_QueryStatus qs;
   13487             : 
   13488           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   13489             :                                              "get_wire_accounts",
   13490             :                                              params,
   13491             :                                              &get_wire_accounts_cb,
   13492             :                                              &ctx);
   13493           0 :   if (GNUNET_OK != ctx.status)
   13494           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   13495           0 :   return qs;
   13496             : 
   13497             : }
   13498             : 
   13499             : 
   13500             : /**
   13501             :  * Closure for #get_wire_fees_cb().
   13502             :  */
   13503             : struct GetWireFeesContext
   13504             : {
   13505             :   /**
   13506             :    * Function to call per result.
   13507             :    */
   13508             :   TALER_EXCHANGEDB_WireFeeCallback cb;
   13509             : 
   13510             :   /**
   13511             :    * Closure for @e cb.
   13512             :    */
   13513             :   void *cb_cls;
   13514             : 
   13515             :   /**
   13516             :    * Plugin context.
   13517             :    */
   13518             :   struct PostgresClosure *pg;
   13519             : 
   13520             :   /**
   13521             :    * Flag set to #GNUNET_OK as long as everything is fine.
   13522             :    */
   13523             :   enum GNUNET_GenericReturnValue status;
   13524             : 
   13525             : };
   13526             : 
   13527             : 
   13528             : /**
   13529             :  * Invoke the callback for each result.
   13530             :  *
   13531             :  * @param cls a `struct GetWireFeesContext *`
   13532             :  * @param result SQL result
   13533             :  * @param num_results number of rows in @a result
   13534             :  */
   13535             : static void
   13536           0 : get_wire_fees_cb (void *cls,
   13537             :                   PGresult *result,
   13538             :                   unsigned int num_results)
   13539             : {
   13540           0 :   struct GetWireFeesContext *ctx = cls;
   13541           0 :   struct PostgresClosure *pg = ctx->pg;
   13542             : 
   13543           0 :   for (unsigned int i = 0; i < num_results; i++)
   13544             :   {
   13545             :     struct TALER_MasterSignatureP master_sig;
   13546             :     struct TALER_WireFeeSet fees;
   13547             :     struct GNUNET_TIME_Timestamp start_date;
   13548             :     struct GNUNET_TIME_Timestamp end_date;
   13549           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   13550           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
   13551             :                                    &fees.wire),
   13552           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
   13553             :                                    &fees.closing),
   13554           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("wad_fee",
   13555             :                                    &fees.wad),
   13556           0 :       GNUNET_PQ_result_spec_timestamp ("start_date",
   13557             :                                        &start_date),
   13558           0 :       GNUNET_PQ_result_spec_timestamp ("end_date",
   13559             :                                        &end_date),
   13560           0 :       GNUNET_PQ_result_spec_auto_from_type ("master_sig",
   13561             :                                             &master_sig),
   13562             :       GNUNET_PQ_result_spec_end
   13563             :     };
   13564             : 
   13565           0 :     if (GNUNET_OK !=
   13566           0 :         GNUNET_PQ_extract_result (result,
   13567             :                                   rs,
   13568             :                                   i))
   13569             :     {
   13570           0 :       GNUNET_break (0);
   13571           0 :       ctx->status = GNUNET_SYSERR;
   13572           0 :       return;
   13573             :     }
   13574           0 :     ctx->cb (ctx->cb_cls,
   13575             :              &fees,
   13576             :              start_date,
   13577             :              end_date,
   13578             :              &master_sig);
   13579           0 :     GNUNET_PQ_cleanup_result (rs);
   13580             :   }
   13581             : }
   13582             : 
   13583             : 
   13584             : /**
   13585             :  * Obtain information about the fee structure of the exchange for
   13586             :  * a given @a wire_method
   13587             :  *
   13588             :  * @param cls closure
   13589             :  * @param wire_method which wire method to obtain fees for
   13590             :  * @param cb function to call on each account
   13591             :  * @param cb_cls closure for @a cb
   13592             :  * @return transaction status code
   13593             :  */
   13594             : static enum GNUNET_DB_QueryStatus
   13595           0 : postgres_get_wire_fees (void *cls,
   13596             :                         const char *wire_method,
   13597             :                         TALER_EXCHANGEDB_WireFeeCallback cb,
   13598             :                         void *cb_cls)
   13599             : {
   13600           0 :   struct PostgresClosure *pg = cls;
   13601           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13602           0 :     GNUNET_PQ_query_param_string (wire_method),
   13603             :     GNUNET_PQ_query_param_end
   13604             :   };
   13605           0 :   struct GetWireFeesContext ctx = {
   13606             :     .cb = cb,
   13607             :     .cb_cls = cb_cls,
   13608             :     .pg = pg,
   13609             :     .status = GNUNET_OK
   13610             :   };
   13611             :   enum GNUNET_DB_QueryStatus qs;
   13612             : 
   13613           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   13614             :                                              "get_wire_fees",
   13615             :                                              params,
   13616             :                                              &get_wire_fees_cb,
   13617             :                                              &ctx);
   13618           0 :   if (GNUNET_OK != ctx.status)
   13619           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   13620           0 :   return qs;
   13621             : }
   13622             : 
   13623             : 
   13624             : /**
   13625             :  * Store information about a revoked online signing key.
   13626             :  *
   13627             :  * @param cls closure
   13628             :  * @param exchange_pub exchange online signing key that was revoked
   13629             :  * @param master_sig signature affirming the revocation
   13630             :  * @return transaction status code
   13631             :  */
   13632             : static enum GNUNET_DB_QueryStatus
   13633           0 : postgres_insert_signkey_revocation (
   13634             :   void *cls,
   13635             :   const struct TALER_ExchangePublicKeyP *exchange_pub,
   13636             :   const struct TALER_MasterSignatureP *master_sig)
   13637             : {
   13638           0 :   struct PostgresClosure *pg = cls;
   13639           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13640           0 :     GNUNET_PQ_query_param_auto_from_type (exchange_pub),
   13641           0 :     GNUNET_PQ_query_param_auto_from_type (master_sig),
   13642             :     GNUNET_PQ_query_param_end
   13643             :   };
   13644             : 
   13645           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   13646             :                                              "insert_signkey_revocation",
   13647             :                                              params);
   13648             : }
   13649             : 
   13650             : 
   13651             : /**
   13652             :  * Obtain information about a revoked online signing key.
   13653             :  *
   13654             :  * @param cls closure
   13655             :  * @param exchange_pub exchange online signing key
   13656             :  * @param[out] master_sig set to signature affirming the revocation (if revoked)
   13657             :  * @return transaction status code
   13658             :  */
   13659             : static enum GNUNET_DB_QueryStatus
   13660           0 : postgres_lookup_signkey_revocation (
   13661             :   void *cls,
   13662             :   const struct TALER_ExchangePublicKeyP *exchange_pub,
   13663             :   struct TALER_MasterSignatureP *master_sig)
   13664             : {
   13665           0 :   struct PostgresClosure *pg = cls;
   13666           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13667           0 :     GNUNET_PQ_query_param_auto_from_type (exchange_pub),
   13668             :     GNUNET_PQ_query_param_end
   13669             :   };
   13670           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   13671           0 :     GNUNET_PQ_result_spec_auto_from_type ("master_sig",
   13672             :                                           master_sig),
   13673             :     GNUNET_PQ_result_spec_end
   13674             :   };
   13675             : 
   13676           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   13677             :                                                    "lookup_signkey_revocation",
   13678             :                                                    params,
   13679             :                                                    rs);
   13680             : }
   13681             : 
   13682             : 
   13683             : /**
   13684             :  * Lookup information about current denomination key.
   13685             :  *
   13686             :  * @param cls closure
   13687             :  * @param h_denom_pub hash of the denomination public key
   13688             :  * @param[out] meta set to various meta data about the key
   13689             :  * @return transaction status code
   13690             :  */
   13691             : static enum GNUNET_DB_QueryStatus
   13692           0 : postgres_lookup_denomination_key (
   13693             :   void *cls,
   13694             :   const struct TALER_DenominationHashP *h_denom_pub,
   13695             :   struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta)
   13696             : {
   13697           0 :   struct PostgresClosure *pg = cls;
   13698           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13699           0 :     GNUNET_PQ_query_param_auto_from_type (h_denom_pub),
   13700             :     GNUNET_PQ_query_param_end
   13701             :   };
   13702           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   13703           0 :     GNUNET_PQ_result_spec_timestamp ("valid_from",
   13704             :                                      &meta->start),
   13705           0 :     GNUNET_PQ_result_spec_timestamp ("expire_withdraw",
   13706             :                                      &meta->expire_withdraw),
   13707           0 :     GNUNET_PQ_result_spec_timestamp ("expire_deposit",
   13708             :                                      &meta->expire_deposit),
   13709           0 :     GNUNET_PQ_result_spec_timestamp ("expire_legal",
   13710             :                                      &meta->expire_legal),
   13711           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("coin",
   13712             :                                  &meta->value),
   13713           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
   13714             :                                  &meta->fees.withdraw),
   13715           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
   13716             :                                  &meta->fees.deposit),
   13717           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
   13718             :                                  &meta->fees.refresh),
   13719           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
   13720             :                                  &meta->fees.refund),
   13721           0 :     GNUNET_PQ_result_spec_uint32 ("age_mask",
   13722             :                                   &meta->age_mask.bits),
   13723             :     GNUNET_PQ_result_spec_end
   13724             :   };
   13725             : 
   13726           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   13727             :                                                    "lookup_denomination_key",
   13728             :                                                    params,
   13729             :                                                    rs);
   13730             : }
   13731             : 
   13732             : 
   13733             : /**
   13734             :  * Activate denomination key, turning it into a "current" or "valid"
   13735             :  * denomination key by adding the master signature.
   13736             :  *
   13737             :  * @param cls closure
   13738             :  * @param h_denom_pub hash of the denomination public key
   13739             :  * @param denom_pub the actual denomination key
   13740             :  * @param meta meta data about the denomination
   13741             :  * @param master_sig master signature to add
   13742             :  * @return transaction status code
   13743             :  */
   13744             : static enum GNUNET_DB_QueryStatus
   13745           0 : postgres_add_denomination_key (
   13746             :   void *cls,
   13747             :   const struct TALER_DenominationHashP *h_denom_pub,
   13748             :   const struct TALER_DenominationPublicKey *denom_pub,
   13749             :   const struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta,
   13750             :   const struct TALER_MasterSignatureP *master_sig)
   13751             : {
   13752           0 :   struct PostgresClosure *pg = cls;
   13753           0 :   struct GNUNET_PQ_QueryParam iparams[] = {
   13754           0 :     GNUNET_PQ_query_param_auto_from_type (h_denom_pub),
   13755           0 :     TALER_PQ_query_param_denom_pub (denom_pub),
   13756           0 :     GNUNET_PQ_query_param_auto_from_type (master_sig),
   13757           0 :     GNUNET_PQ_query_param_timestamp (&meta->start),
   13758           0 :     GNUNET_PQ_query_param_timestamp (&meta->expire_withdraw),
   13759           0 :     GNUNET_PQ_query_param_timestamp (&meta->expire_deposit),
   13760           0 :     GNUNET_PQ_query_param_timestamp (&meta->expire_legal),
   13761           0 :     TALER_PQ_query_param_amount (&meta->value),
   13762           0 :     TALER_PQ_query_param_amount (&meta->fees.withdraw),
   13763           0 :     TALER_PQ_query_param_amount (&meta->fees.deposit),
   13764           0 :     TALER_PQ_query_param_amount (&meta->fees.refresh),
   13765           0 :     TALER_PQ_query_param_amount (&meta->fees.refund),
   13766           0 :     GNUNET_PQ_query_param_uint32 (&meta->age_mask.bits),
   13767             :     GNUNET_PQ_query_param_end
   13768             :   };
   13769             : 
   13770             :   /* Sanity check: ensure fees match coin currency */
   13771           0 :   GNUNET_assert (GNUNET_YES ==
   13772             :                  TALER_denom_fee_check_currency (meta->value.currency,
   13773             :                                                  &meta->fees));
   13774           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   13775             :                                              "denomination_insert",
   13776             :                                              iparams);
   13777             : }
   13778             : 
   13779             : 
   13780             : /**
   13781             :  * Add signing key.
   13782             :  *
   13783             :  * @param cls closure
   13784             :  * @param exchange_pub the exchange online signing public key
   13785             :  * @param meta meta data about @a exchange_pub
   13786             :  * @param master_sig master signature to add
   13787             :  * @return transaction status code
   13788             :  */
   13789             : static enum GNUNET_DB_QueryStatus
   13790           0 : postgres_activate_signing_key (
   13791             :   void *cls,
   13792             :   const struct TALER_ExchangePublicKeyP *exchange_pub,
   13793             :   const struct TALER_EXCHANGEDB_SignkeyMetaData *meta,
   13794             :   const struct TALER_MasterSignatureP *master_sig)
   13795             : {
   13796           0 :   struct PostgresClosure *pg = cls;
   13797           0 :   struct GNUNET_PQ_QueryParam iparams[] = {
   13798           0 :     GNUNET_PQ_query_param_auto_from_type (exchange_pub),
   13799           0 :     GNUNET_PQ_query_param_timestamp (&meta->start),
   13800           0 :     GNUNET_PQ_query_param_timestamp (&meta->expire_sign),
   13801           0 :     GNUNET_PQ_query_param_timestamp (&meta->expire_legal),
   13802           0 :     GNUNET_PQ_query_param_auto_from_type (master_sig),
   13803             :     GNUNET_PQ_query_param_end
   13804             :   };
   13805             : 
   13806           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   13807             :                                              "insert_signkey",
   13808             :                                              iparams);
   13809             : }
   13810             : 
   13811             : 
   13812             : /**
   13813             :  * Lookup signing key meta data.
   13814             :  *
   13815             :  * @param cls closure
   13816             :  * @param exchange_pub the exchange online signing public key
   13817             :  * @param[out] meta meta data about @a exchange_pub
   13818             :  * @return transaction status code
   13819             :  */
   13820             : static enum GNUNET_DB_QueryStatus
   13821           0 : postgres_lookup_signing_key (
   13822             :   void *cls,
   13823             :   const struct TALER_ExchangePublicKeyP *exchange_pub,
   13824             :   struct TALER_EXCHANGEDB_SignkeyMetaData *meta)
   13825             : {
   13826           0 :   struct PostgresClosure *pg = cls;
   13827           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13828           0 :     GNUNET_PQ_query_param_auto_from_type (exchange_pub),
   13829             :     GNUNET_PQ_query_param_end
   13830             :   };
   13831           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   13832           0 :     GNUNET_PQ_result_spec_timestamp ("valid_from",
   13833             :                                      &meta->start),
   13834           0 :     GNUNET_PQ_result_spec_timestamp ("expire_sign",
   13835             :                                      &meta->expire_sign),
   13836           0 :     GNUNET_PQ_result_spec_timestamp ("expire_legal",
   13837             :                                      &meta->expire_legal),
   13838             :     GNUNET_PQ_result_spec_end
   13839             :   };
   13840             : 
   13841           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   13842             :                                                    "lookup_signing_key",
   13843             :                                                    params,
   13844             :                                                    rs);
   13845             : }
   13846             : 
   13847             : 
   13848             : /**
   13849             :  * Insert information about an auditor auditing a denomination key.
   13850             :  *
   13851             :  * @param cls closure
   13852             :  * @param h_denom_pub the audited denomination
   13853             :  * @param auditor_pub the auditor's key
   13854             :  * @param auditor_sig signature affirming the auditor's audit activity
   13855             :  * @return transaction status code
   13856             :  */
   13857             : static enum GNUNET_DB_QueryStatus
   13858           0 : postgres_insert_auditor_denom_sig (
   13859             :   void *cls,
   13860             :   const struct TALER_DenominationHashP *h_denom_pub,
   13861             :   const struct TALER_AuditorPublicKeyP *auditor_pub,
   13862             :   const struct TALER_AuditorSignatureP *auditor_sig)
   13863             : {
   13864           0 :   struct PostgresClosure *pg = cls;
   13865           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13866           0 :     GNUNET_PQ_query_param_auto_from_type (auditor_pub),
   13867           0 :     GNUNET_PQ_query_param_auto_from_type (h_denom_pub),
   13868           0 :     GNUNET_PQ_query_param_auto_from_type (auditor_sig),
   13869             :     GNUNET_PQ_query_param_end
   13870             :   };
   13871             : 
   13872           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   13873             :                                              "insert_auditor_denom_sig",
   13874             :                                              params);
   13875             : }
   13876             : 
   13877             : 
   13878             : /**
   13879             :  * Select information about an auditor auditing a denomination key.
   13880             :  *
   13881             :  * @param cls closure
   13882             :  * @param h_denom_pub the audited denomination
   13883             :  * @param auditor_pub the auditor's key
   13884             :  * @param[out] auditor_sig set to signature affirming the auditor's audit activity
   13885             :  * @return transaction status code
   13886             :  */
   13887             : static enum GNUNET_DB_QueryStatus
   13888           0 : postgres_select_auditor_denom_sig (
   13889             :   void *cls,
   13890             :   const struct TALER_DenominationHashP *h_denom_pub,
   13891             :   const struct TALER_AuditorPublicKeyP *auditor_pub,
   13892             :   struct TALER_AuditorSignatureP *auditor_sig)
   13893             : {
   13894           0 :   struct PostgresClosure *pg = cls;
   13895           0 :   struct GNUNET_PQ_QueryParam params[] = {
   13896           0 :     GNUNET_PQ_query_param_auto_from_type (auditor_pub),
   13897           0 :     GNUNET_PQ_query_param_auto_from_type (h_denom_pub),
   13898             :     GNUNET_PQ_query_param_end
   13899             :   };
   13900           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   13901           0 :     GNUNET_PQ_result_spec_auto_from_type ("auditor_sig",
   13902             :                                           auditor_sig),
   13903             :     GNUNET_PQ_result_spec_end
   13904             :   };
   13905             : 
   13906           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   13907             :                                                    "select_auditor_denom_sig",
   13908             :                                                    params,
   13909             :                                                    rs);
   13910             : }
   13911             : 
   13912             : 
   13913             : /**
   13914             :  * Closure for #wire_fee_by_time_helper()
   13915             :  */
   13916             : struct WireFeeLookupContext
   13917             : {
   13918             : 
   13919             :   /**
   13920             :    * Set to the wire fees. Set to invalid if fees conflict over
   13921             :    * the given time period.
   13922             :    */
   13923             :   struct TALER_WireFeeSet *fees;
   13924             : 
   13925             :   /**
   13926             :    * Plugin context.
   13927             :    */
   13928             :   struct PostgresClosure *pg;
   13929             : };
   13930             : 
   13931             : 
   13932             : /**
   13933             :  * Helper function for #postgres_lookup_wire_fee_by_time().
   13934             :  * Calls the callback with the wire fee structure.
   13935             :  *
   13936             :  * @param cls a `struct WireFeeLookupContext`
   13937             :  * @param result db results
   13938             :  * @param num_results number of results in @a result
   13939             :  */
   13940             : static void
   13941           0 : wire_fee_by_time_helper (void *cls,
   13942             :                          PGresult *result,
   13943             :                          unsigned int num_results)
   13944             : {
   13945           0 :   struct WireFeeLookupContext *wlc = cls;
   13946           0 :   struct PostgresClosure *pg = wlc->pg;
   13947             : 
   13948           0 :   for (unsigned int i = 0; i<num_results; i++)
   13949             :   {
   13950             :     struct TALER_WireFeeSet fs;
   13951           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   13952           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
   13953             :                                    &fs.wire),
   13954           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
   13955             :                                    &fs.closing),
   13956           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("wad_fee",
   13957             :                                    &fs.wad),
   13958             :       GNUNET_PQ_result_spec_end
   13959             :     };
   13960             : 
   13961           0 :     if (GNUNET_OK !=
   13962           0 :         GNUNET_PQ_extract_result (result,
   13963             :                                   rs,
   13964             :                                   i))
   13965             :     {
   13966           0 :       GNUNET_break (0);
   13967             :       /* invalidate */
   13968           0 :       memset (wlc->fees,
   13969             :               0,
   13970             :               sizeof (struct TALER_WireFeeSet));
   13971           0 :       return;
   13972             :     }
   13973           0 :     if (0 == i)
   13974             :     {
   13975           0 :       *wlc->fees = fs;
   13976           0 :       continue;
   13977             :     }
   13978           0 :     if (0 !=
   13979           0 :         TALER_wire_fee_set_cmp (&fs,
   13980           0 :                                 wlc->fees))
   13981             :     {
   13982             :       /* invalidate */
   13983           0 :       memset (wlc->fees,
   13984             :               0,
   13985             :               sizeof (struct TALER_WireFeeSet));
   13986           0 :       return;
   13987             :     }
   13988             :   }
   13989             : }
   13990             : 
   13991             : 
   13992             : /**
   13993             :  * Lookup information about known wire fees.  Finds all applicable
   13994             :  * fees in the given range. If they are identical, returns the
   13995             :  * respective @a fees. If any of the fees
   13996             :  * differ between @a start_time and @a end_time, the transaction
   13997             :  * succeeds BUT returns an invalid amount for both fees.
   13998             :  *
   13999             :  * @param cls closure
   14000             :  * @param wire_method the wire method to lookup fees for
   14001             :  * @param start_time starting time of fee
   14002             :  * @param end_time end time of fee
   14003             :  * @param[out] fees wire fees for that time period; if
   14004             :  *             different fees exists within this time
   14005             :  *             period, an 'invalid' amount is returned.
   14006             :  * @return transaction status code
   14007             :  */
   14008             : static enum GNUNET_DB_QueryStatus
   14009           0 : postgres_lookup_wire_fee_by_time (
   14010             :   void *cls,
   14011             :   const char *wire_method,
   14012             :   struct GNUNET_TIME_Timestamp start_time,
   14013             :   struct GNUNET_TIME_Timestamp end_time,
   14014             :   struct TALER_WireFeeSet *fees)
   14015             : {
   14016           0 :   struct PostgresClosure *pg = cls;
   14017           0 :   struct GNUNET_PQ_QueryParam params[] = {
   14018           0 :     GNUNET_PQ_query_param_string (wire_method),
   14019           0 :     GNUNET_PQ_query_param_timestamp (&start_time),
   14020           0 :     GNUNET_PQ_query_param_timestamp (&end_time),
   14021             :     GNUNET_PQ_query_param_end
   14022             :   };
   14023           0 :   struct WireFeeLookupContext wlc = {
   14024             :     .fees = fees,
   14025             :     .pg = pg
   14026             :   };
   14027             : 
   14028           0 :   return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   14029             :                                                "lookup_wire_fee_by_time",
   14030             :                                                params,
   14031             :                                                &wire_fee_by_time_helper,
   14032             :                                                &wlc);
   14033             : }
   14034             : 
   14035             : 
   14036             : /**
   14037             :  * Closure for #global_fee_by_time_helper()
   14038             :  */
   14039             : struct GlobalFeeLookupContext
   14040             : {
   14041             : 
   14042             :   /**
   14043             :    * Set to the wire fees. Set to invalid if fees conflict over
   14044             :    * the given time period.
   14045             :    */
   14046             :   struct TALER_GlobalFeeSet *fees;
   14047             : 
   14048             :   /**
   14049             :    * Set to timeout of unmerged purses
   14050             :    */
   14051             :   struct GNUNET_TIME_Relative *purse_timeout;
   14052             : 
   14053             :   /**
   14054             :    * Set to timeout of accounts without kyc.
   14055             :    */
   14056             :   struct GNUNET_TIME_Relative *kyc_timeout;
   14057             : 
   14058             :   /**
   14059             :    * Set to history expiration for reserves.
   14060             :    */
   14061             :   struct GNUNET_TIME_Relative *history_expiration;
   14062             : 
   14063             :   /**
   14064             :    * Set to number of free purses per account.
   14065             :    */
   14066             :   uint32_t *purse_account_limit;
   14067             : 
   14068             :   /**
   14069             :    * Plugin context.
   14070             :    */
   14071             :   struct PostgresClosure *pg;
   14072             : };
   14073             : 
   14074             : 
   14075             : /**
   14076             :  * Helper function for #postgres_lookup_global_fee_by_time().
   14077             :  * Calls the callback with each denomination key.
   14078             :  *
   14079             :  * @param cls a `struct GlobalFeeLookupContext`
   14080             :  * @param result db results
   14081             :  * @param num_results number of results in @a result
   14082             :  */
   14083             : static void
   14084           0 : global_fee_by_time_helper (void *cls,
   14085             :                            PGresult *result,
   14086             :                            unsigned int num_results)
   14087             : {
   14088           0 :   struct GlobalFeeLookupContext *wlc = cls;
   14089           0 :   struct PostgresClosure *pg = wlc->pg;
   14090             : 
   14091           0 :   for (unsigned int i = 0; i<num_results; i++)
   14092             :   {
   14093             :     struct TALER_GlobalFeeSet fs;
   14094             :     struct GNUNET_TIME_Relative purse_timeout;
   14095             :     struct GNUNET_TIME_Relative kyc_timeout;
   14096             :     struct GNUNET_TIME_Relative history_expiration;
   14097             :     uint32_t purse_account_limit;
   14098           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   14099           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
   14100             :                                    &fs.history),
   14101           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("kyc_fee",
   14102             :                                    &fs.kyc),
   14103           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("account_fee",
   14104             :                                    &fs.account),
   14105           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee",
   14106             :                                    &fs.purse),
   14107           0 :       GNUNET_PQ_result_spec_relative_time ("purse_timeout",
   14108             :                                            &purse_timeout),
   14109           0 :       GNUNET_PQ_result_spec_relative_time ("kyc_timeout",
   14110             :                                            &kyc_timeout),
   14111           0 :       GNUNET_PQ_result_spec_relative_time ("history_expiration",
   14112             :                                            &history_expiration),
   14113           0 :       GNUNET_PQ_result_spec_uint32 ("purse_account_limit",
   14114             :                                     &purse_account_limit),
   14115             :       GNUNET_PQ_result_spec_end
   14116             :     };
   14117             : 
   14118           0 :     if (GNUNET_OK !=
   14119           0 :         GNUNET_PQ_extract_result (result,
   14120             :                                   rs,
   14121             :                                   i))
   14122             :     {
   14123           0 :       GNUNET_break (0);
   14124             :       /* invalidate */
   14125           0 :       memset (wlc->fees,
   14126             :               0,
   14127             :               sizeof (struct TALER_GlobalFeeSet));
   14128           0 :       return;
   14129             :     }
   14130           0 :     if (0 == i)
   14131             :     {
   14132           0 :       *wlc->fees = fs;
   14133           0 :       *wlc->purse_timeout = purse_timeout;
   14134           0 :       *wlc->kyc_timeout = kyc_timeout;
   14135           0 :       *wlc->history_expiration = history_expiration;
   14136           0 :       *wlc->purse_account_limit = purse_account_limit;
   14137           0 :       continue;
   14138             :     }
   14139           0 :     if ( (0 !=
   14140           0 :           TALER_global_fee_set_cmp (&fs,
   14141           0 :                                     wlc->fees)) ||
   14142           0 :          (purse_account_limit != *wlc->purse_account_limit) ||
   14143           0 :          (GNUNET_TIME_relative_cmp (purse_timeout,
   14144             :                                     !=,
   14145           0 :                                     *wlc->purse_timeout)) ||
   14146           0 :          (GNUNET_TIME_relative_cmp (kyc_timeout,
   14147             :                                     !=,
   14148           0 :                                     *wlc->kyc_timeout)) ||
   14149           0 :          (GNUNET_TIME_relative_cmp (history_expiration,
   14150             :                                     !=,
   14151             :                                     *wlc->history_expiration)) )
   14152             :     {
   14153             :       /* invalidate */
   14154           0 :       memset (wlc->fees,
   14155             :               0,
   14156             :               sizeof (struct TALER_GlobalFeeSet));
   14157           0 :       return;
   14158             :     }
   14159             :   }
   14160             : }
   14161             : 
   14162             : 
   14163             : /**
   14164             :  * Lookup information about known global fees.
   14165             :  *
   14166             :  * @param cls closure
   14167             :  * @param start_time starting time of fee
   14168             :  * @param end_time end time of fee
   14169             :  * @param[out] fees set to wire fees for that time period; if
   14170             :  *             different global fee exists within this time
   14171             :  *             period, an 'invalid' amount is returned.
   14172             :  * @param[out] purse_timeout set to when unmerged purses expire
   14173             :  * @param[out] kyc_timeout set to when reserves without kyc expire
   14174             :  * @param[out] history_expiration set to when we expire reserve histories
   14175             :  * @param[out] purse_account_limit set to number of free purses
   14176             :  * @return transaction status code
   14177             :  */
   14178             : static enum GNUNET_DB_QueryStatus
   14179           0 : postgres_lookup_global_fee_by_time (
   14180             :   void *cls,
   14181             :   struct GNUNET_TIME_Timestamp start_time,
   14182             :   struct GNUNET_TIME_Timestamp end_time,
   14183             :   struct TALER_GlobalFeeSet *fees,
   14184             :   struct GNUNET_TIME_Relative *purse_timeout,
   14185             :   struct GNUNET_TIME_Relative *kyc_timeout,
   14186             :   struct GNUNET_TIME_Relative *history_expiration,
   14187             :   uint32_t *purse_account_limit)
   14188             : {
   14189           0 :   struct PostgresClosure *pg = cls;
   14190           0 :   struct GNUNET_PQ_QueryParam params[] = {
   14191           0 :     GNUNET_PQ_query_param_timestamp (&start_time),
   14192           0 :     GNUNET_PQ_query_param_timestamp (&end_time),
   14193             :     GNUNET_PQ_query_param_end
   14194             :   };
   14195           0 :   struct GlobalFeeLookupContext wlc = {
   14196             :     .fees = fees,
   14197             :     .purse_timeout = purse_timeout,
   14198             :     .kyc_timeout = kyc_timeout,
   14199             :     .history_expiration = history_expiration,
   14200             :     .purse_account_limit = purse_account_limit,
   14201             :     .pg = pg
   14202             :   };
   14203             : 
   14204           0 :   return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   14205             :                                                "lookup_global_fee_by_time",
   14206             :                                                params,
   14207             :                                                &global_fee_by_time_helper,
   14208             :                                                &wlc);
   14209             : }
   14210             : 
   14211             : 
   14212             : /**
   14213             :  * Lookup the latest serial number of @a table.  Used in
   14214             :  * exchange-auditor database replication.
   14215             :  *
   14216             :  * @param cls closure
   14217             :  * @param table table for which we should return the serial
   14218             :  * @param[out] serial latest serial number in use
   14219             :  * @return transaction status code, GNUNET_DB_STATUS_HARD_ERROR if
   14220             :  *         @a table does not have a serial number
   14221             :  */
   14222             : static enum GNUNET_DB_QueryStatus
   14223           0 : postgres_lookup_serial_by_table (void *cls,
   14224             :                                  enum TALER_EXCHANGEDB_ReplicatedTable table,
   14225             :                                  uint64_t *serial)
   14226             : {
   14227           0 :   struct PostgresClosure *pg = cls;
   14228           0 :   struct GNUNET_PQ_QueryParam params[] = {
   14229             :     GNUNET_PQ_query_param_end
   14230             :   };
   14231           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   14232           0 :     GNUNET_PQ_result_spec_uint64 ("serial",
   14233             :                                   serial),
   14234             :     GNUNET_PQ_result_spec_end
   14235             :   };
   14236             :   const char *statement;
   14237             : 
   14238           0 :   switch (table)
   14239             :   {
   14240           0 :   case TALER_EXCHANGEDB_RT_DENOMINATIONS:
   14241           0 :     statement = "select_serial_by_table_denominations";
   14242           0 :     break;
   14243           0 :   case TALER_EXCHANGEDB_RT_DENOMINATION_REVOCATIONS:
   14244           0 :     statement = "select_serial_by_table_denomination_revocations";
   14245           0 :     break;
   14246           0 :   case TALER_EXCHANGEDB_RT_WIRE_TARGETS:
   14247           0 :     statement = "select_serial_by_table_wire_targets";
   14248           0 :     break;
   14249           0 :   case TALER_EXCHANGEDB_RT_RESERVES:
   14250           0 :     statement = "select_serial_by_table_reserves";
   14251           0 :     break;
   14252           0 :   case TALER_EXCHANGEDB_RT_RESERVES_IN:
   14253           0 :     statement = "select_serial_by_table_reserves_in";
   14254           0 :     break;
   14255           0 :   case TALER_EXCHANGEDB_RT_RESERVES_CLOSE:
   14256           0 :     statement = "select_serial_by_table_reserves_close";
   14257           0 :     break;
   14258           0 :   case TALER_EXCHANGEDB_RT_RESERVES_OUT:
   14259           0 :     statement = "select_serial_by_table_reserves_out";
   14260           0 :     break;
   14261           0 :   case TALER_EXCHANGEDB_RT_AUDITORS:
   14262           0 :     statement = "select_serial_by_table_auditors";
   14263           0 :     break;
   14264           0 :   case TALER_EXCHANGEDB_RT_AUDITOR_DENOM_SIGS:
   14265           0 :     statement = "select_serial_by_table_auditor_denom_sigs";
   14266           0 :     break;
   14267           0 :   case TALER_EXCHANGEDB_RT_EXCHANGE_SIGN_KEYS:
   14268           0 :     statement = "select_serial_by_table_exchange_sign_keys";
   14269           0 :     break;
   14270           0 :   case TALER_EXCHANGEDB_RT_SIGNKEY_REVOCATIONS:
   14271           0 :     statement = "select_serial_by_table_signkey_revocations";
   14272           0 :     break;
   14273           0 :   case TALER_EXCHANGEDB_RT_KNOWN_COINS:
   14274           0 :     statement = "select_serial_by_table_known_coins";
   14275           0 :     break;
   14276           0 :   case TALER_EXCHANGEDB_RT_REFRESH_COMMITMENTS:
   14277           0 :     statement = "select_serial_by_table_refresh_commitments";
   14278           0 :     break;
   14279           0 :   case TALER_EXCHANGEDB_RT_REFRESH_REVEALED_COINS:
   14280           0 :     statement = "select_serial_by_table_refresh_revealed_coins";
   14281           0 :     break;
   14282           0 :   case TALER_EXCHANGEDB_RT_REFRESH_TRANSFER_KEYS:
   14283           0 :     statement = "select_serial_by_table_refresh_transfer_keys";
   14284           0 :     break;
   14285           0 :   case TALER_EXCHANGEDB_RT_DEPOSITS:
   14286           0 :     statement = "select_serial_by_table_deposits";
   14287           0 :     break;
   14288           0 :   case TALER_EXCHANGEDB_RT_REFUNDS:
   14289           0 :     statement = "select_serial_by_table_refunds";
   14290           0 :     break;
   14291           0 :   case TALER_EXCHANGEDB_RT_WIRE_OUT:
   14292           0 :     statement = "select_serial_by_table_wire_out";
   14293           0 :     break;
   14294           0 :   case TALER_EXCHANGEDB_RT_AGGREGATION_TRACKING:
   14295           0 :     statement = "select_serial_by_table_aggregation_tracking";
   14296           0 :     break;
   14297           0 :   case TALER_EXCHANGEDB_RT_WIRE_FEE:
   14298           0 :     statement = "select_serial_by_table_wire_fee";
   14299           0 :     break;
   14300           0 :   case TALER_EXCHANGEDB_RT_GLOBAL_FEE:
   14301           0 :     statement = "select_serial_by_table_global_fee";
   14302           0 :     break;
   14303           0 :   case TALER_EXCHANGEDB_RT_RECOUP:
   14304           0 :     statement = "select_serial_by_table_recoup";
   14305           0 :     break;
   14306           0 :   case TALER_EXCHANGEDB_RT_RECOUP_REFRESH:
   14307           0 :     statement = "select_serial_by_table_recoup_refresh";
   14308           0 :     break;
   14309           0 :   case TALER_EXCHANGEDB_RT_EXTENSIONS:
   14310           0 :     statement = "select_serial_by_table_extensions";
   14311           0 :     break;
   14312           0 :   case TALER_EXCHANGEDB_RT_EXTENSION_DETAILS:
   14313           0 :     statement = "select_serial_by_table_extension_details";
   14314           0 :     break;
   14315           0 :   case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
   14316           0 :     statement = "select_serial_by_table_purse_requests";
   14317           0 :     break;
   14318           0 :   case TALER_EXCHANGEDB_RT_PURSE_REFUNDS:
   14319           0 :     statement = "select_serial_by_table_purse_refunds";
   14320           0 :     break;
   14321           0 :   case TALER_EXCHANGEDB_RT_PURSE_MERGES:
   14322           0 :     statement = "select_serial_by_table_purse_merges";
   14323           0 :     break;
   14324           0 :   case TALER_EXCHANGEDB_RT_PURSE_DEPOSITS:
   14325           0 :     statement = "select_serial_by_table_purse_deposits";
   14326           0 :     break;
   14327           0 :   case TALER_EXCHANGEDB_RT_ACCOUNT_MERGES:
   14328           0 :     statement = "select_serial_by_table_account_merges";
   14329           0 :     break;
   14330           0 :   case TALER_EXCHANGEDB_RT_HISTORY_REQUESTS:
   14331           0 :     statement = "select_serial_by_table_history_requests";
   14332           0 :     break;
   14333           0 :   case TALER_EXCHANGEDB_RT_CLOSE_REQUESTS:
   14334           0 :     statement = "select_serial_by_table_close_requests";
   14335           0 :     break;
   14336           0 :   case TALER_EXCHANGEDB_RT_WADS_OUT:
   14337           0 :     statement = "select_serial_by_table_wads_out";
   14338           0 :     break;
   14339           0 :   case TALER_EXCHANGEDB_RT_WADS_OUT_ENTRIES:
   14340           0 :     statement = "select_serial_by_table_wads_out_entries";
   14341           0 :     break;
   14342           0 :   case TALER_EXCHANGEDB_RT_WADS_IN:
   14343           0 :     statement = "select_serial_by_table_wads_in";
   14344           0 :     break;
   14345           0 :   case TALER_EXCHANGEDB_RT_WADS_IN_ENTRIES:
   14346           0 :     statement = "select_serial_by_table_wads_in_entries";
   14347           0 :     break;
   14348           0 :   case TALER_EXCHANGEDB_RT_PROFIT_DRAINS:
   14349           0 :     statement = "select_serial_by_table_profit_drains";
   14350           0 :     break;
   14351           0 :   default:
   14352           0 :     GNUNET_break (0);
   14353           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   14354             :   }
   14355             : 
   14356           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   14357             :                                                    statement,
   14358             :                                                    params,
   14359             :                                                    rs);
   14360             : }
   14361             : 
   14362             : 
   14363             : /**
   14364             :  * Closure for callbacks used by #postgres_lookup_records_by_table.
   14365             :  */
   14366             : struct LookupRecordsByTableContext
   14367             : {
   14368             :   /**
   14369             :    * Plugin context.
   14370             :    */
   14371             :   struct PostgresClosure *pg;
   14372             : 
   14373             :   /**
   14374             :    * Function to call with the results.
   14375             :    */
   14376             :   TALER_EXCHANGEDB_ReplicationCallback cb;
   14377             : 
   14378             :   /**
   14379             :    * Closure for @a cb.
   14380             :    */
   14381             :   void *cb_cls;
   14382             : 
   14383             :   /**
   14384             :    * Set to true on errors.
   14385             :    */
   14386             :   bool error;
   14387             : };
   14388             : 
   14389             : 
   14390             : #include "lrbt_callbacks.c"
   14391             : 
   14392             : 
   14393             : /**
   14394             :  * Lookup records above @a serial number in @a table. Used in
   14395             :  * exchange-auditor database replication.
   14396             :  *
   14397             :  * @param cls closure
   14398             :  * @param table table for which we should return the serial
   14399             :  * @param serial largest serial number to exclude
   14400             :  * @param cb function to call on the records
   14401             :  * @param cb_cls closure for @a cb
   14402             :  * @return transaction status code, GNUNET_DB_STATUS_HARD_ERROR if
   14403             :  *         @a table does not have a serial number
   14404             :  */
   14405             : static enum GNUNET_DB_QueryStatus
   14406           0 : postgres_lookup_records_by_table (void *cls,
   14407             :                                   enum TALER_EXCHANGEDB_ReplicatedTable table,
   14408             :                                   uint64_t serial,
   14409             :                                   TALER_EXCHANGEDB_ReplicationCallback cb,
   14410             :                                   void *cb_cls)
   14411             : {
   14412           0 :   struct PostgresClosure *pg = cls;
   14413           0 :   struct GNUNET_PQ_QueryParam params[] = {
   14414           0 :     GNUNET_PQ_query_param_uint64 (&serial),
   14415             :     GNUNET_PQ_query_param_end
   14416             :   };
   14417           0 :   struct LookupRecordsByTableContext ctx = {
   14418             :     .pg = pg,
   14419             :     .cb = cb,
   14420             :     .cb_cls = cb_cls
   14421             :   };
   14422             :   GNUNET_PQ_PostgresResultHandler rh;
   14423             :   const char *statement;
   14424             :   enum GNUNET_DB_QueryStatus qs;
   14425             : 
   14426           0 :   switch (table)
   14427             :   {
   14428           0 :   case TALER_EXCHANGEDB_RT_DENOMINATIONS:
   14429           0 :     statement = "select_above_serial_by_table_denominations";
   14430           0 :     rh = &lrbt_cb_table_denominations;
   14431           0 :     break;
   14432           0 :   case TALER_EXCHANGEDB_RT_DENOMINATION_REVOCATIONS:
   14433           0 :     statement = "select_above_serial_by_table_denomination_revocations";
   14434           0 :     rh = &lrbt_cb_table_denomination_revocations;
   14435           0 :     break;
   14436           0 :   case TALER_EXCHANGEDB_RT_WIRE_TARGETS:
   14437           0 :     statement = "select_above_serial_by_table_wire_targets";
   14438           0 :     rh = &lrbt_cb_table_wire_targets;
   14439           0 :     break;
   14440           0 :   case TALER_EXCHANGEDB_RT_RESERVES:
   14441           0 :     statement = "select_above_serial_by_table_reserves";
   14442           0 :     rh = &lrbt_cb_table_reserves;
   14443           0 :     break;
   14444           0 :   case TALER_EXCHANGEDB_RT_RESERVES_IN:
   14445           0 :     statement = "select_above_serial_by_table_reserves_in";
   14446           0 :     rh = &lrbt_cb_table_reserves_in;
   14447           0 :     break;
   14448           0 :   case TALER_EXCHANGEDB_RT_RESERVES_CLOSE:
   14449           0 :     statement = "select_above_serial_by_table_reserves_close";
   14450           0 :     rh = &lrbt_cb_table_reserves_close;
   14451           0 :     break;
   14452           0 :   case TALER_EXCHANGEDB_RT_RESERVES_OUT:
   14453           0 :     statement = "select_above_serial_by_table_reserves_out";
   14454           0 :     rh = &lrbt_cb_table_reserves_out;
   14455           0 :     break;
   14456           0 :   case TALER_EXCHANGEDB_RT_AUDITORS:
   14457           0 :     statement = "select_above_serial_by_table_auditors";
   14458           0 :     rh = &lrbt_cb_table_auditors;
   14459           0 :     break;
   14460           0 :   case TALER_EXCHANGEDB_RT_AUDITOR_DENOM_SIGS:
   14461           0 :     statement = "select_above_serial_by_table_auditor_denom_sigs";
   14462           0 :     rh = &lrbt_cb_table_auditor_denom_sigs;
   14463           0 :     break;
   14464           0 :   case TALER_EXCHANGEDB_RT_EXCHANGE_SIGN_KEYS:
   14465           0 :     statement = "select_above_serial_by_table_exchange_sign_keys";
   14466           0 :     rh = &lrbt_cb_table_exchange_sign_keys;
   14467           0 :     break;
   14468           0 :   case TALER_EXCHANGEDB_RT_SIGNKEY_REVOCATIONS:
   14469           0 :     statement = "select_above_serial_by_table_signkey_revocations";
   14470           0 :     rh = &lrbt_cb_table_signkey_revocations;
   14471           0 :     break;
   14472           0 :   case TALER_EXCHANGEDB_RT_KNOWN_COINS:
   14473           0 :     statement = "select_above_serial_by_table_known_coins";
   14474           0 :     rh = &lrbt_cb_table_known_coins;
   14475           0 :     break;
   14476           0 :   case TALER_EXCHANGEDB_RT_REFRESH_COMMITMENTS:
   14477           0 :     statement = "select_above_serial_by_table_refresh_commitments";
   14478           0 :     rh = &lrbt_cb_table_refresh_commitments;
   14479           0 :     break;
   14480           0 :   case TALER_EXCHANGEDB_RT_REFRESH_REVEALED_COINS:
   14481           0 :     statement = "select_above_serial_by_table_refresh_revealed_coins";
   14482           0 :     rh = &lrbt_cb_table_refresh_revealed_coins;
   14483           0 :     break;
   14484           0 :   case TALER_EXCHANGEDB_RT_REFRESH_TRANSFER_KEYS:
   14485           0 :     statement = "select_above_serial_by_table_refresh_transfer_keys";
   14486           0 :     rh = &lrbt_cb_table_refresh_transfer_keys;
   14487           0 :     break;
   14488           0 :   case TALER_EXCHANGEDB_RT_DEPOSITS:
   14489           0 :     statement = "select_above_serial_by_table_deposits";
   14490           0 :     rh = &lrbt_cb_table_deposits;
   14491           0 :     break;
   14492           0 :   case TALER_EXCHANGEDB_RT_REFUNDS:
   14493           0 :     statement = "select_above_serial_by_table_refunds";
   14494           0 :     rh = &lrbt_cb_table_refunds;
   14495           0 :     break;
   14496           0 :   case TALER_EXCHANGEDB_RT_WIRE_OUT:
   14497           0 :     statement = "select_above_serial_by_table_wire_out";
   14498           0 :     rh = &lrbt_cb_table_wire_out;
   14499           0 :     break;
   14500           0 :   case TALER_EXCHANGEDB_RT_AGGREGATION_TRACKING:
   14501           0 :     statement = "select_above_serial_by_table_aggregation_tracking";
   14502           0 :     rh = &lrbt_cb_table_aggregation_tracking;
   14503           0 :     break;
   14504           0 :   case TALER_EXCHANGEDB_RT_WIRE_FEE:
   14505           0 :     statement = "select_above_serial_by_table_wire_fee";
   14506           0 :     rh = &lrbt_cb_table_wire_fee;
   14507           0 :     break;
   14508           0 :   case TALER_EXCHANGEDB_RT_GLOBAL_FEE:
   14509           0 :     statement = "select_above_serial_by_table_global_fee";
   14510           0 :     rh = &lrbt_cb_table_global_fee;
   14511           0 :     break;
   14512           0 :   case TALER_EXCHANGEDB_RT_RECOUP:
   14513           0 :     statement = "select_above_serial_by_table_recoup";
   14514           0 :     rh = &lrbt_cb_table_recoup;
   14515           0 :     break;
   14516           0 :   case TALER_EXCHANGEDB_RT_RECOUP_REFRESH:
   14517           0 :     statement = "select_above_serial_by_table_recoup_refresh";
   14518           0 :     rh = &lrbt_cb_table_recoup_refresh;
   14519           0 :     break;
   14520           0 :   case TALER_EXCHANGEDB_RT_EXTENSIONS:
   14521           0 :     statement = "select_above_serial_by_table_extensions";
   14522           0 :     rh = &lrbt_cb_table_extensions;
   14523           0 :     break;
   14524           0 :   case TALER_EXCHANGEDB_RT_EXTENSION_DETAILS:
   14525           0 :     statement = "select_above_serial_by_table_extension_details";
   14526           0 :     rh = &lrbt_cb_table_extension_details;
   14527           0 :     break;
   14528           0 :   case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
   14529           0 :     statement = "select_above_serial_by_table_purse_requests";
   14530           0 :     rh = &lrbt_cb_table_purse_requests;
   14531           0 :     break;
   14532           0 :   case TALER_EXCHANGEDB_RT_PURSE_REFUNDS:
   14533           0 :     statement = "select_above_serial_by_table_purse_refunds";
   14534           0 :     rh = &lrbt_cb_table_purse_refunds;
   14535           0 :     break;
   14536           0 :   case TALER_EXCHANGEDB_RT_PURSE_MERGES:
   14537           0 :     statement = "select_above_serial_by_table_purse_merges";
   14538           0 :     rh = &lrbt_cb_table_purse_merges;
   14539           0 :     break;
   14540           0 :   case TALER_EXCHANGEDB_RT_PURSE_DEPOSITS:
   14541           0 :     statement = "select_above_serial_by_table_purse_deposits";
   14542           0 :     rh = &lrbt_cb_table_purse_deposits;
   14543           0 :     break;
   14544           0 :   case TALER_EXCHANGEDB_RT_ACCOUNT_MERGES:
   14545           0 :     statement = "select_above_serial_by_table_account_merges";
   14546           0 :     rh = &lrbt_cb_table_account_merges;
   14547           0 :     break;
   14548           0 :   case TALER_EXCHANGEDB_RT_HISTORY_REQUESTS:
   14549           0 :     statement = "select_above_serial_by_table_history_requests";
   14550           0 :     rh = &lrbt_cb_table_history_requests;
   14551           0 :     break;
   14552           0 :   case TALER_EXCHANGEDB_RT_CLOSE_REQUESTS:
   14553           0 :     statement = "select_above_serial_by_table_close_requests";
   14554           0 :     rh = &lrbt_cb_table_close_requests;
   14555           0 :     break;
   14556           0 :   case TALER_EXCHANGEDB_RT_WADS_OUT:
   14557           0 :     statement = "select_above_serial_by_table_wads_out";
   14558           0 :     rh = &lrbt_cb_table_wads_out;
   14559           0 :     break;
   14560           0 :   case TALER_EXCHANGEDB_RT_WADS_OUT_ENTRIES:
   14561           0 :     statement = "select_above_serial_by_table_wads_out_entries";
   14562           0 :     rh = &lrbt_cb_table_wads_out_entries;
   14563           0 :     break;
   14564           0 :   case TALER_EXCHANGEDB_RT_WADS_IN:
   14565           0 :     statement = "select_above_serial_by_table_wads_in";
   14566           0 :     rh = &lrbt_cb_table_wads_in;
   14567           0 :     break;
   14568           0 :   case TALER_EXCHANGEDB_RT_WADS_IN_ENTRIES:
   14569           0 :     statement = "select_above_serial_by_table_wads_in_entries";
   14570           0 :     rh = &lrbt_cb_table_wads_in_entries;
   14571           0 :     break;
   14572           0 :   case TALER_EXCHANGEDB_RT_PROFIT_DRAINS:
   14573           0 :     statement = "select_above_serial_by_table_profit_drains";
   14574           0 :     rh = &lrbt_cb_table_profit_drains;
   14575           0 :     break;
   14576           0 :   default:
   14577           0 :     GNUNET_break (0);
   14578           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   14579             :   }
   14580             : 
   14581           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   14582             :                                              statement,
   14583             :                                              params,
   14584             :                                              rh,
   14585             :                                              &ctx);
   14586           0 :   if (qs < 0)
   14587             :   {
   14588           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   14589             :                 "Failed to run `%s'\n",
   14590             :                 statement);
   14591           0 :     return qs;
   14592             :   }
   14593           0 :   if (ctx.error)
   14594             :   {
   14595           0 :     GNUNET_break (0);
   14596           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   14597             :   }
   14598           0 :   return qs;
   14599             : }
   14600             : 
   14601             : 
   14602             : /**
   14603             :  * Signature of helper functions of #postgres_insert_records_by_table.
   14604             :  *
   14605             :  * @param pg plugin context
   14606             :  * @param td record to insert
   14607             :  * @return transaction status code
   14608             :  */
   14609             : typedef enum GNUNET_DB_QueryStatus
   14610             : (*InsertRecordCallback)(struct PostgresClosure *pg,
   14611             :                         const struct TALER_EXCHANGEDB_TableData *td);
   14612             : 
   14613             : 
   14614             : #include "irbt_callbacks.c"
   14615             : 
   14616             : 
   14617             : /**
   14618             :  * Insert record set into @a table.  Used in exchange-auditor database
   14619             :  * replication.
   14620             :  *
   14621             :  * @param cls closure
   14622             :  * @param td table data to insert
   14623             :  * @return transaction status code, #GNUNET_DB_STATUS_HARD_ERROR if
   14624             :  *         @e table in @a tr is not supported
   14625             :  */
   14626             : static enum GNUNET_DB_QueryStatus
   14627           0 : postgres_insert_records_by_table (void *cls,
   14628             :                                   const struct TALER_EXCHANGEDB_TableData *td)
   14629             : {
   14630           0 :   struct PostgresClosure *pg = cls;
   14631             :   InsertRecordCallback rh;
   14632             : 
   14633           0 :   switch (td->table)
   14634             :   {
   14635           0 :   case TALER_EXCHANGEDB_RT_DENOMINATIONS:
   14636           0 :     rh = &irbt_cb_table_denominations;
   14637           0 :     break;
   14638           0 :   case TALER_EXCHANGEDB_RT_DENOMINATION_REVOCATIONS:
   14639           0 :     rh = &irbt_cb_table_denomination_revocations;
   14640           0 :     break;
   14641           0 :   case TALER_EXCHANGEDB_RT_WIRE_TARGETS:
   14642           0 :     rh = &irbt_cb_table_wire_targets;
   14643           0 :     break;
   14644           0 :   case TALER_EXCHANGEDB_RT_RESERVES:
   14645           0 :     rh = &irbt_cb_table_reserves;
   14646           0 :     break;
   14647           0 :   case TALER_EXCHANGEDB_RT_RESERVES_IN:
   14648           0 :     rh = &irbt_cb_table_reserves_in;
   14649           0 :     break;
   14650           0 :   case TALER_EXCHANGEDB_RT_RESERVES_CLOSE:
   14651           0 :     rh = &irbt_cb_table_reserves_close;
   14652           0 :     break;
   14653           0 :   case TALER_EXCHANGEDB_RT_RESERVES_OUT:
   14654           0 :     rh = &irbt_cb_table_reserves_out;
   14655           0 :     break;
   14656           0 :   case TALER_EXCHANGEDB_RT_AUDITORS:
   14657           0 :     rh = &irbt_cb_table_auditors;
   14658           0 :     break;
   14659           0 :   case TALER_EXCHANGEDB_RT_AUDITOR_DENOM_SIGS:
   14660           0 :     rh = &irbt_cb_table_auditor_denom_sigs;
   14661           0 :     break;
   14662           0 :   case TALER_EXCHANGEDB_RT_EXCHANGE_SIGN_KEYS:
   14663           0 :     rh = &irbt_cb_table_exchange_sign_keys;
   14664           0 :     break;
   14665           0 :   case TALER_EXCHANGEDB_RT_SIGNKEY_REVOCATIONS:
   14666           0 :     rh = &irbt_cb_table_signkey_revocations;
   14667           0 :     break;
   14668           0 :   case TALER_EXCHANGEDB_RT_KNOWN_COINS:
   14669           0 :     rh = &irbt_cb_table_known_coins;
   14670           0 :     break;
   14671           0 :   case TALER_EXCHANGEDB_RT_REFRESH_COMMITMENTS:
   14672           0 :     rh = &irbt_cb_table_refresh_commitments;
   14673           0 :     break;
   14674           0 :   case TALER_EXCHANGEDB_RT_REFRESH_REVEALED_COINS:
   14675           0 :     rh = &irbt_cb_table_refresh_revealed_coins;
   14676           0 :     break;
   14677           0 :   case TALER_EXCHANGEDB_RT_REFRESH_TRANSFER_KEYS:
   14678           0 :     rh = &irbt_cb_table_refresh_transfer_keys;
   14679           0 :     break;
   14680           0 :   case TALER_EXCHANGEDB_RT_DEPOSITS:
   14681           0 :     rh = &irbt_cb_table_deposits;
   14682           0 :     break;
   14683           0 :   case TALER_EXCHANGEDB_RT_REFUNDS:
   14684           0 :     rh = &irbt_cb_table_refunds;
   14685           0 :     break;
   14686           0 :   case TALER_EXCHANGEDB_RT_WIRE_OUT:
   14687           0 :     rh = &irbt_cb_table_wire_out;
   14688           0 :     break;
   14689           0 :   case TALER_EXCHANGEDB_RT_AGGREGATION_TRACKING:
   14690           0 :     rh = &irbt_cb_table_aggregation_tracking;
   14691           0 :     break;
   14692           0 :   case TALER_EXCHANGEDB_RT_WIRE_FEE:
   14693           0 :     rh = &irbt_cb_table_wire_fee;
   14694           0 :     break;
   14695           0 :   case TALER_EXCHANGEDB_RT_GLOBAL_FEE:
   14696           0 :     rh = &irbt_cb_table_global_fee;
   14697           0 :     break;
   14698           0 :   case TALER_EXCHANGEDB_RT_RECOUP:
   14699           0 :     rh = &irbt_cb_table_recoup;
   14700           0 :     break;
   14701           0 :   case TALER_EXCHANGEDB_RT_RECOUP_REFRESH:
   14702           0 :     rh = &irbt_cb_table_recoup_refresh;
   14703           0 :     break;
   14704           0 :   case TALER_EXCHANGEDB_RT_EXTENSIONS:
   14705           0 :     rh = &irbt_cb_table_extensions;
   14706           0 :     break;
   14707           0 :   case TALER_EXCHANGEDB_RT_EXTENSION_DETAILS:
   14708           0 :     rh = &irbt_cb_table_extension_details;
   14709           0 :     break;
   14710           0 :   case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
   14711           0 :     rh = &irbt_cb_table_purse_requests;
   14712           0 :     break;
   14713           0 :   case TALER_EXCHANGEDB_RT_PURSE_REFUNDS:
   14714           0 :     rh = &irbt_cb_table_purse_refunds;
   14715           0 :     break;
   14716           0 :   case TALER_EXCHANGEDB_RT_PURSE_MERGES:
   14717           0 :     rh = &irbt_cb_table_purse_merges;
   14718           0 :     break;
   14719           0 :   case TALER_EXCHANGEDB_RT_PURSE_DEPOSITS:
   14720           0 :     rh = &irbt_cb_table_purse_deposits;
   14721           0 :     break;
   14722           0 :   case TALER_EXCHANGEDB_RT_ACCOUNT_MERGES:
   14723           0 :     rh = &irbt_cb_table_account_mergers;
   14724           0 :     break;
   14725           0 :   case TALER_EXCHANGEDB_RT_HISTORY_REQUESTS:
   14726           0 :     rh = &irbt_cb_table_history_requests;
   14727           0 :     break;
   14728           0 :   case TALER_EXCHANGEDB_RT_CLOSE_REQUESTS:
   14729           0 :     rh = &irbt_cb_table_close_requests;
   14730           0 :     break;
   14731           0 :   case TALER_EXCHANGEDB_RT_WADS_OUT:
   14732           0 :     rh = &irbt_cb_table_wads_out;
   14733           0 :     break;
   14734           0 :   case TALER_EXCHANGEDB_RT_WADS_OUT_ENTRIES:
   14735           0 :     rh = &irbt_cb_table_wads_out_entries;
   14736           0 :     break;
   14737           0 :   case TALER_EXCHANGEDB_RT_WADS_IN:
   14738           0 :     rh = &irbt_cb_table_wads_in;
   14739           0 :     break;
   14740           0 :   case TALER_EXCHANGEDB_RT_WADS_IN_ENTRIES:
   14741           0 :     rh = &irbt_cb_table_wads_in_entries;
   14742           0 :     break;
   14743           0 :   case TALER_EXCHANGEDB_RT_PROFIT_DRAINS:
   14744           0 :     rh = &irbt_cb_table_profit_drains;
   14745           0 :     break;
   14746           0 :   default:
   14747           0 :     GNUNET_break (0);
   14748           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   14749             :   }
   14750           0 :   return rh (pg,
   14751             :              td);
   14752             : }
   14753             : 
   14754             : 
   14755             : /**
   14756             :  * Function called to grab a work shard on an operation @a op. Runs in its
   14757             :  * own transaction.
   14758             :  *
   14759             :  * @param cls the @e cls of this struct with the plugin-specific state
   14760             :  * @param job_name name of the operation to grab a word shard for
   14761             :  * @param delay minimum age of a shard to grab
   14762             :  * @param shard_size desired shard size
   14763             :  * @param[out] start_row inclusive start row of the shard (returned)
   14764             :  * @param[out] end_row exclusive end row of the shard (returned)
   14765             :  * @return transaction status code
   14766             :  */
   14767             : static enum GNUNET_DB_QueryStatus
   14768           0 : postgres_begin_shard (void *cls,
   14769             :                       const char *job_name,
   14770             :                       struct GNUNET_TIME_Relative delay,
   14771             :                       uint64_t shard_size,
   14772             :                       uint64_t *start_row,
   14773             :                       uint64_t *end_row)
   14774             : {
   14775           0 :   struct PostgresClosure *pg = cls;
   14776             : 
   14777           0 :   for (unsigned int retries = 0; retries<10; retries++)
   14778             :   {
   14779           0 :     if (GNUNET_OK !=
   14780           0 :         postgres_start (pg,
   14781             :                         "begin_shard"))
   14782             :     {
   14783           0 :       GNUNET_break (0);
   14784           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
   14785             :     }
   14786             : 
   14787             :     {
   14788             :       struct GNUNET_TIME_Absolute past;
   14789             :       enum GNUNET_DB_QueryStatus qs;
   14790           0 :       struct GNUNET_PQ_QueryParam params[] = {
   14791           0 :         GNUNET_PQ_query_param_string (job_name),
   14792           0 :         GNUNET_PQ_query_param_absolute_time (&past),
   14793             :         GNUNET_PQ_query_param_end
   14794             :       };
   14795           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
   14796           0 :         GNUNET_PQ_result_spec_uint64 ("start_row",
   14797             :                                       start_row),
   14798           0 :         GNUNET_PQ_result_spec_uint64 ("end_row",
   14799             :                                       end_row),
   14800             :         GNUNET_PQ_result_spec_end
   14801             :       };
   14802             : 
   14803           0 :       past = GNUNET_TIME_absolute_get ();
   14804           0 :       qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   14805             :                                                      "get_open_shard",
   14806             :                                                      params,
   14807             :                                                      rs);
   14808           0 :       switch (qs)
   14809             :       {
   14810           0 :       case GNUNET_DB_STATUS_HARD_ERROR:
   14811           0 :         GNUNET_break (0);
   14812           0 :         postgres_rollback (pg);
   14813           0 :         return qs;
   14814           0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
   14815           0 :         postgres_rollback (pg);
   14816           0 :         continue;
   14817           0 :       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   14818             :         {
   14819             :           enum GNUNET_DB_QueryStatus qs;
   14820             :           struct GNUNET_TIME_Absolute now;
   14821           0 :           struct GNUNET_PQ_QueryParam params[] = {
   14822           0 :             GNUNET_PQ_query_param_string (job_name),
   14823           0 :             GNUNET_PQ_query_param_absolute_time (&now),
   14824           0 :             GNUNET_PQ_query_param_uint64 (start_row),
   14825           0 :             GNUNET_PQ_query_param_uint64 (end_row),
   14826             :             GNUNET_PQ_query_param_end
   14827             :           };
   14828             : 
   14829           0 :           now = GNUNET_TIME_relative_to_absolute (delay);
   14830           0 :           qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
   14831             :                                                    "reclaim_shard",
   14832             :                                                    params);
   14833             :           switch (qs)
   14834             :           {
   14835           0 :           case GNUNET_DB_STATUS_HARD_ERROR:
   14836           0 :             GNUNET_break (0);
   14837           0 :             postgres_rollback (pg);
   14838           0 :             return qs;
   14839           0 :           case GNUNET_DB_STATUS_SOFT_ERROR:
   14840           0 :             postgres_rollback (pg);
   14841           0 :             continue;
   14842           0 :           case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   14843           0 :             goto commit;
   14844           0 :           case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   14845           0 :             GNUNET_break (0); /* logic error, should be impossible */
   14846           0 :             postgres_rollback (pg);
   14847           0 :             return GNUNET_DB_STATUS_HARD_ERROR;
   14848             :           }
   14849             :         }
   14850           0 :         break; /* actually unreachable */
   14851           0 :       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   14852           0 :         break; /* continued below */
   14853             :       }
   14854           0 :     } /* get_open_shard */
   14855             : 
   14856             :     /* No open shard, find last 'end_row' */
   14857             :     {
   14858             :       enum GNUNET_DB_QueryStatus qs;
   14859           0 :       struct GNUNET_PQ_QueryParam params[] = {
   14860           0 :         GNUNET_PQ_query_param_string (job_name),
   14861             :         GNUNET_PQ_query_param_end
   14862             :       };
   14863           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
   14864           0 :         GNUNET_PQ_result_spec_uint64 ("end_row",
   14865             :                                       start_row),
   14866             :         GNUNET_PQ_result_spec_end
   14867             :       };
   14868             : 
   14869           0 :       qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   14870             :                                                      "get_last_shard",
   14871             :                                                      params,
   14872             :                                                      rs);
   14873           0 :       switch (qs)
   14874             :       {
   14875           0 :       case GNUNET_DB_STATUS_HARD_ERROR:
   14876           0 :         GNUNET_break (0);
   14877           0 :         postgres_rollback (pg);
   14878           0 :         return qs;
   14879           0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
   14880           0 :         postgres_rollback (pg);
   14881           0 :         continue;
   14882           0 :       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   14883           0 :         break;
   14884           0 :       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   14885           0 :         *start_row = 0; /* base-case: no shards yet */
   14886           0 :         break; /* continued below */
   14887             :       }
   14888           0 :       *end_row = *start_row + shard_size;
   14889             :     } /* get_last_shard */
   14890             : 
   14891             :     /* Claim fresh shard */
   14892             :     {
   14893             :       enum GNUNET_DB_QueryStatus qs;
   14894             :       struct GNUNET_TIME_Absolute now;
   14895           0 :       struct GNUNET_PQ_QueryParam params[] = {
   14896           0 :         GNUNET_PQ_query_param_string (job_name),
   14897           0 :         GNUNET_PQ_query_param_absolute_time (&now),
   14898           0 :         GNUNET_PQ_query_param_uint64 (start_row),
   14899           0 :         GNUNET_PQ_query_param_uint64 (end_row),
   14900             :         GNUNET_PQ_query_param_end
   14901             :       };
   14902             : 
   14903           0 :       now = GNUNET_TIME_relative_to_absolute (delay);
   14904           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   14905             :                   "Trying to claim shard (%llu-%llu]\n",
   14906             :                   (unsigned long long) *start_row,
   14907             :                   (unsigned long long) *end_row);
   14908           0 :       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
   14909             :                                                "claim_next_shard",
   14910             :                                                params);
   14911           0 :       switch (qs)
   14912             :       {
   14913           0 :       case GNUNET_DB_STATUS_HARD_ERROR:
   14914           0 :         GNUNET_break (0);
   14915           0 :         postgres_rollback (pg);
   14916           0 :         return qs;
   14917           0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
   14918           0 :         postgres_rollback (pg);
   14919           0 :         continue;
   14920           0 :       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   14921             :         /* continued below */
   14922           0 :         break;
   14923           0 :       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   14924             :         /* someone else got this shard already,
   14925             :            try again */
   14926           0 :         postgres_rollback (pg);
   14927           0 :         continue;
   14928             :       }
   14929           0 :     } /* claim_next_shard */
   14930             : 
   14931             :     /* commit */
   14932           0 : commit:
   14933             :     {
   14934             :       enum GNUNET_DB_QueryStatus qs;
   14935             : 
   14936           0 :       qs = postgres_commit (pg);
   14937           0 :       switch (qs)
   14938             :       {
   14939           0 :       case GNUNET_DB_STATUS_HARD_ERROR:
   14940           0 :         GNUNET_break (0);
   14941           0 :         postgres_rollback (pg);
   14942           0 :         return qs;
   14943           0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
   14944           0 :         postgres_rollback (pg);
   14945           0 :         continue;
   14946           0 :       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   14947             :       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   14948           0 :         return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   14949             :       }
   14950             :     }
   14951             :   } /* retry 'for' loop */
   14952           0 :   return GNUNET_DB_STATUS_SOFT_ERROR;
   14953             : }
   14954             : 
   14955             : 
   14956             : /**
   14957             :  * Function called to abort work on a shard.
   14958             :  *
   14959             :  * @param cls the @e cls of this struct with the plugin-specific state
   14960             :  * @param job_name name of the operation to abort a word shard for
   14961             :  * @param start_row inclusive start row of the shard
   14962             :  * @param end_row exclusive end row of the shard
   14963             :  * @return transaction status code
   14964             :  */
   14965             : static enum GNUNET_DB_QueryStatus
   14966           0 : postgres_abort_shard (void *cls,
   14967             :                       const char *job_name,
   14968             :                       uint64_t start_row,
   14969             :                       uint64_t end_row)
   14970             : {
   14971           0 :   struct PostgresClosure *pg = cls;
   14972           0 :   struct GNUNET_PQ_QueryParam params[] = {
   14973           0 :     GNUNET_PQ_query_param_string (job_name),
   14974           0 :     GNUNET_PQ_query_param_uint64 (&start_row),
   14975           0 :     GNUNET_PQ_query_param_uint64 (&end_row),
   14976             :     GNUNET_PQ_query_param_end
   14977             :   };
   14978             : 
   14979           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   14980             :                                              "abort_shard",
   14981             :                                              params);
   14982             : }
   14983             : 
   14984             : 
   14985             : /**
   14986             :  * Function called to persist that work on a shard was completed.
   14987             :  *
   14988             :  * @param cls the @e cls of this struct with the plugin-specific state
   14989             :  * @param job_name name of the operation to grab a word shard for
   14990             :  * @param start_row inclusive start row of the shard
   14991             :  * @param end_row exclusive end row of the shard
   14992             :  * @return transaction status code
   14993             :  */
   14994             : enum GNUNET_DB_QueryStatus
   14995           0 : postgres_complete_shard (void *cls,
   14996             :                          const char *job_name,
   14997             :                          uint64_t start_row,
   14998             :                          uint64_t end_row)
   14999             : {
   15000           0 :   struct PostgresClosure *pg = cls;
   15001             : 
   15002           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15003           0 :     GNUNET_PQ_query_param_string (job_name),
   15004           0 :     GNUNET_PQ_query_param_uint64 (&start_row),
   15005           0 :     GNUNET_PQ_query_param_uint64 (&end_row),
   15006             :     GNUNET_PQ_query_param_end
   15007             :   };
   15008             : 
   15009           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   15010             :               "Completing shard %llu-%llu\n",
   15011             :               (unsigned long long) start_row,
   15012             :               (unsigned long long) end_row);
   15013           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   15014             :                                              "complete_shard",
   15015             :                                              params);
   15016             : }
   15017             : 
   15018             : 
   15019             : /**
   15020             :  * Function called to grab a revolving work shard on an operation @a op. Runs
   15021             :  * in its own transaction. Returns the oldest inactive shard.
   15022             :  *
   15023             :  * @param cls the @e cls of this struct with the plugin-specific state
   15024             :  * @param job_name name of the operation to grab a revolving shard for
   15025             :  * @param shard_size desired shard size
   15026             :  * @param shard_limit exclusive end of the shard range
   15027             :  * @param[out] start_row inclusive start row of the shard (returned)
   15028             :  * @param[out] end_row inclusive end row of the shard (returned)
   15029             :  * @return transaction status code
   15030             :  */
   15031             : static enum GNUNET_DB_QueryStatus
   15032           0 : postgres_begin_revolving_shard (void *cls,
   15033             :                                 const char *job_name,
   15034             :                                 uint32_t shard_size,
   15035             :                                 uint32_t shard_limit,
   15036             :                                 uint32_t *start_row,
   15037             :                                 uint32_t *end_row)
   15038             : {
   15039           0 :   struct PostgresClosure *pg = cls;
   15040             : 
   15041           0 :   GNUNET_assert (shard_limit <= 1U + (uint32_t) INT_MAX);
   15042           0 :   GNUNET_assert (shard_limit > 0);
   15043           0 :   GNUNET_assert (shard_size > 0);
   15044           0 :   for (unsigned int retries = 0; retries<3; retries++)
   15045             :   {
   15046           0 :     if (GNUNET_OK !=
   15047           0 :         postgres_start (pg,
   15048             :                         "begin_revolving_shard"))
   15049             :     {
   15050           0 :       GNUNET_break (0);
   15051           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
   15052             :     }
   15053             : 
   15054             :     /* First, find last 'end_row' */
   15055             :     {
   15056             :       enum GNUNET_DB_QueryStatus qs;
   15057             :       uint32_t last_end;
   15058           0 :       struct GNUNET_PQ_QueryParam params[] = {
   15059           0 :         GNUNET_PQ_query_param_string (job_name),
   15060             :         GNUNET_PQ_query_param_end
   15061             :       };
   15062           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
   15063           0 :         GNUNET_PQ_result_spec_uint32 ("end_row",
   15064             :                                       &last_end),
   15065             :         GNUNET_PQ_result_spec_end
   15066             :       };
   15067             : 
   15068           0 :       qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   15069             :                                                      "get_last_revolving_shard",
   15070             :                                                      params,
   15071             :                                                      rs);
   15072           0 :       switch (qs)
   15073             :       {
   15074           0 :       case GNUNET_DB_STATUS_HARD_ERROR:
   15075           0 :         GNUNET_break (0);
   15076           0 :         postgres_rollback (pg);
   15077           0 :         return qs;
   15078           0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
   15079           0 :         postgres_rollback (pg);
   15080           0 :         continue;
   15081           0 :       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   15082           0 :         *start_row = 1U + last_end;
   15083           0 :         break;
   15084           0 :       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   15085           0 :         *start_row = 0; /* base-case: no shards yet */
   15086           0 :         break; /* continued below */
   15087             :       }
   15088           0 :     } /* get_last_shard */
   15089             : 
   15090           0 :     if (*start_row < shard_limit)
   15091             :     {
   15092             :       /* Claim fresh shard */
   15093             :       enum GNUNET_DB_QueryStatus qs;
   15094             :       struct GNUNET_TIME_Absolute now;
   15095           0 :       struct GNUNET_PQ_QueryParam params[] = {
   15096           0 :         GNUNET_PQ_query_param_string (job_name),
   15097           0 :         GNUNET_PQ_query_param_absolute_time (&now),
   15098           0 :         GNUNET_PQ_query_param_uint32 (start_row),
   15099           0 :         GNUNET_PQ_query_param_uint32 (end_row),
   15100             :         GNUNET_PQ_query_param_end
   15101             :       };
   15102             : 
   15103           0 :       *end_row = GNUNET_MIN (shard_limit,
   15104             :                              *start_row + shard_size - 1);
   15105           0 :       now = GNUNET_TIME_absolute_get ();
   15106           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   15107             :                   "Trying to claim shard %llu-%llu\n",
   15108             :                   (unsigned long long) *start_row,
   15109             :                   (unsigned long long) *end_row);
   15110           0 :       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
   15111             :                                                "create_revolving_shard",
   15112             :                                                params);
   15113           0 :       switch (qs)
   15114             :       {
   15115           0 :       case GNUNET_DB_STATUS_HARD_ERROR:
   15116           0 :         GNUNET_break (0);
   15117           0 :         postgres_rollback (pg);
   15118           0 :         return qs;
   15119           0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
   15120           0 :         postgres_rollback (pg);
   15121           0 :         continue;
   15122           0 :       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   15123             :         /* continued below (with commit) */
   15124           0 :         break;
   15125           0 :       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   15126             :         /* someone else got this shard already,
   15127             :            try again */
   15128           0 :         postgres_rollback (pg);
   15129           0 :         continue;
   15130             :       }
   15131           0 :     } /* end create fresh reovlving shard */
   15132             :     else
   15133             :     {
   15134             :       /* claim oldest existing shard */
   15135             :       enum GNUNET_DB_QueryStatus qs;
   15136           0 :       struct GNUNET_PQ_QueryParam params[] = {
   15137           0 :         GNUNET_PQ_query_param_string (job_name),
   15138             :         GNUNET_PQ_query_param_end
   15139             :       };
   15140           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
   15141           0 :         GNUNET_PQ_result_spec_uint32 ("start_row",
   15142             :                                       start_row),
   15143           0 :         GNUNET_PQ_result_spec_uint32 ("end_row",
   15144             :                                       end_row),
   15145             :         GNUNET_PQ_result_spec_end
   15146             :       };
   15147             : 
   15148           0 :       qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   15149             :                                                      "get_open_revolving_shard",
   15150             :                                                      params,
   15151             :                                                      rs);
   15152           0 :       switch (qs)
   15153             :       {
   15154           0 :       case GNUNET_DB_STATUS_HARD_ERROR:
   15155           0 :         GNUNET_break (0);
   15156           0 :         postgres_rollback (pg);
   15157           0 :         return qs;
   15158           0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
   15159           0 :         postgres_rollback (pg);
   15160           0 :         continue;
   15161           0 :       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   15162             :         /* no open shards available */
   15163           0 :         postgres_rollback (pg);
   15164           0 :         return qs;
   15165           0 :       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   15166             :         {
   15167             :           enum GNUNET_DB_QueryStatus qs;
   15168             :           struct GNUNET_TIME_Timestamp now;
   15169           0 :           struct GNUNET_PQ_QueryParam params[] = {
   15170           0 :             GNUNET_PQ_query_param_string (job_name),
   15171           0 :             GNUNET_PQ_query_param_timestamp (&now),
   15172           0 :             GNUNET_PQ_query_param_uint32 (start_row),
   15173           0 :             GNUNET_PQ_query_param_uint32 (end_row),
   15174             :             GNUNET_PQ_query_param_end
   15175             :           };
   15176             : 
   15177           0 :           now = GNUNET_TIME_timestamp_get ();
   15178           0 :           qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
   15179             :                                                    "reclaim_revolving_shard",
   15180             :                                                    params);
   15181             :           switch (qs)
   15182             :           {
   15183           0 :           case GNUNET_DB_STATUS_HARD_ERROR:
   15184           0 :             GNUNET_break (0);
   15185           0 :             postgres_rollback (pg);
   15186           0 :             return qs;
   15187           0 :           case GNUNET_DB_STATUS_SOFT_ERROR:
   15188           0 :             postgres_rollback (pg);
   15189           0 :             continue;
   15190           0 :           case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   15191           0 :             break; /* continue with commit */
   15192           0 :           case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   15193           0 :             GNUNET_break (0); /* logic error, should be impossible */
   15194           0 :             postgres_rollback (pg);
   15195           0 :             return GNUNET_DB_STATUS_HARD_ERROR;
   15196             :           }
   15197           0 :         }
   15198           0 :         break; /* continue with commit */
   15199             :       }
   15200           0 :     } /* end claim oldest existing shard */
   15201             : 
   15202             :     /* commit */
   15203             :     {
   15204             :       enum GNUNET_DB_QueryStatus qs;
   15205             : 
   15206           0 :       qs = postgres_commit (pg);
   15207           0 :       switch (qs)
   15208             :       {
   15209           0 :       case GNUNET_DB_STATUS_HARD_ERROR:
   15210           0 :         GNUNET_break (0);
   15211           0 :         postgres_rollback (pg);
   15212           0 :         return qs;
   15213           0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
   15214           0 :         postgres_rollback (pg);
   15215           0 :         continue;
   15216           0 :       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   15217             :       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   15218           0 :         return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   15219             :       }
   15220             :     }
   15221             :   } /* retry 'for' loop */
   15222           0 :   return GNUNET_DB_STATUS_SOFT_ERROR;
   15223             : }
   15224             : 
   15225             : 
   15226             : /**
   15227             :  * Function called to release a revolving shard
   15228             :  * back into the work pool.  Clears the
   15229             :  * "completed" flag.
   15230             :  *
   15231             :  * @param cls the @e cls of this struct with the plugin-specific state
   15232             :  * @param job_name name of the operation to grab a word shard for
   15233             :  * @param start_row inclusive start row of the shard
   15234             :  * @param end_row exclusive end row of the shard
   15235             :  * @return transaction status code
   15236             :  */
   15237             : enum GNUNET_DB_QueryStatus
   15238           0 : postgres_release_revolving_shard (void *cls,
   15239             :                                   const char *job_name,
   15240             :                                   uint32_t start_row,
   15241             :                                   uint32_t end_row)
   15242             : {
   15243           0 :   struct PostgresClosure *pg = cls;
   15244           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15245           0 :     GNUNET_PQ_query_param_string (job_name),
   15246           0 :     GNUNET_PQ_query_param_uint32 (&start_row),
   15247           0 :     GNUNET_PQ_query_param_uint32 (&end_row),
   15248             :     GNUNET_PQ_query_param_end
   15249             :   };
   15250             : 
   15251           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   15252             :               "Releasing revolving shard %s %u-%u\n",
   15253             :               job_name,
   15254             :               (unsigned int) start_row,
   15255             :               (unsigned int) end_row);
   15256           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   15257             :                                              "release_revolving_shard",
   15258             :                                              params);
   15259             : }
   15260             : 
   15261             : 
   15262             : /**
   15263             :  * Function called to delete all revolving shards.
   15264             :  * To be used after a crash or when the shard size is
   15265             :  * changed.
   15266             :  *
   15267             :  * @param cls the @e cls of this struct with the plugin-specific state
   15268             :  * @return transaction status code
   15269             :  */
   15270             : enum GNUNET_GenericReturnValue
   15271           0 : postgres_delete_shard_locks (void *cls)
   15272             : {
   15273           0 :   struct PostgresClosure *pg = cls;
   15274           0 :   struct GNUNET_PQ_ExecuteStatement es[] = {
   15275           0 :     GNUNET_PQ_make_execute ("DELETE FROM work_shards;"),
   15276           0 :     GNUNET_PQ_make_execute ("DELETE FROM revolving_work_shards;"),
   15277             :     GNUNET_PQ_EXECUTE_STATEMENT_END
   15278             :   };
   15279             : 
   15280           0 :   return GNUNET_PQ_exec_statements (pg->conn,
   15281             :                                     es);
   15282             : }
   15283             : 
   15284             : 
   15285             : /**
   15286             :  * Function called to save the configuration of an extension
   15287             :  * (age-restriction, peer2peer, ...).  After successful storage of the
   15288             :  * configuration it triggers the corresponding event.
   15289             :  *
   15290             :  * @param cls the @e cls of this struct with the plugin-specific state
   15291             :  * @param extension_name the name of the extension
   15292             :  * @param config JSON object of the configuration as string
   15293             :  * @return transaction status code
   15294             :  */
   15295             : enum GNUNET_DB_QueryStatus
   15296           0 : postgres_set_extension_config (void *cls,
   15297             :                                const char *extension_name,
   15298             :                                const char *config)
   15299             : {
   15300           0 :   struct PostgresClosure *pg = cls;
   15301           0 :   struct GNUNET_PQ_QueryParam pcfg =
   15302           0 :     (NULL == config || 0 == *config)
   15303           0 :     ? GNUNET_PQ_query_param_null ()
   15304           0 :     : GNUNET_PQ_query_param_string (config);
   15305           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15306           0 :     GNUNET_PQ_query_param_string (extension_name),
   15307             :     pcfg,
   15308             :     GNUNET_PQ_query_param_end
   15309             :   };
   15310             : 
   15311           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   15312             :                                              "set_extension_config",
   15313             :                                              params);
   15314             : }
   15315             : 
   15316             : 
   15317             : /**
   15318             :  * Function called to get the configuration of an extension
   15319             :  * (age-restriction, peer2peer, ...)
   15320             :  *
   15321             :  * @param cls the @e cls of this struct with the plugin-specific state
   15322             :  * @param extension_name the name of the extension
   15323             :  * @param[out] config JSON object of the configuration as string
   15324             :  * @return transaction status code
   15325             :  */
   15326             : enum GNUNET_DB_QueryStatus
   15327           0 : postgres_get_extension_config (void *cls,
   15328             :                                const char *extension_name,
   15329             :                                char **config)
   15330             : {
   15331           0 :   struct PostgresClosure *pg = cls;
   15332           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15333           0 :     GNUNET_PQ_query_param_string (extension_name),
   15334             :     GNUNET_PQ_query_param_end
   15335             :   };
   15336             :   bool is_null;
   15337           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   15338           0 :     GNUNET_PQ_result_spec_allow_null (
   15339             :       GNUNET_PQ_result_spec_string ("config",
   15340             :                                     config),
   15341             :       &is_null),
   15342             :     GNUNET_PQ_result_spec_end
   15343             :   };
   15344             : 
   15345           0 :   *config = NULL;
   15346           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   15347             :                                                    "get_extension_config",
   15348             :                                                    params,
   15349             :                                                    rs);
   15350             : }
   15351             : 
   15352             : 
   15353             : /**
   15354             :  * Function called to store configuration data about a partner
   15355             :  * exchange that we are federated with.
   15356             :  *
   15357             :  * @param cls the @e cls of this struct with the plugin-specific state
   15358             :  * @param master_pub public offline signing key of the partner exchange
   15359             :  * @param start_date when does the following data start to be valid
   15360             :  * @param end_date when does the validity end (exclusive)
   15361             :  * @param wad_frequency how often do we do exchange-to-exchange settlements?
   15362             :  * @param wad_fee how much do we charge for transfers to the partner
   15363             :  * @param partner_base_url base URL of the partner exchange
   15364             :  * @param master_sig signature with our offline signing key affirming the above
   15365             :  * @return transaction status code
   15366             :  */
   15367             : static enum GNUNET_DB_QueryStatus
   15368           0 : postgres_insert_partner (void *cls,
   15369             :                          const struct TALER_MasterPublicKeyP *master_pub,
   15370             :                          struct GNUNET_TIME_Timestamp start_date,
   15371             :                          struct GNUNET_TIME_Timestamp end_date,
   15372             :                          struct GNUNET_TIME_Relative wad_frequency,
   15373             :                          const struct TALER_Amount *wad_fee,
   15374             :                          const char *partner_base_url,
   15375             :                          const struct TALER_MasterSignatureP *master_sig)
   15376             : {
   15377           0 :   struct PostgresClosure *pg = cls;
   15378           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15379           0 :     GNUNET_PQ_query_param_auto_from_type (master_pub),
   15380           0 :     GNUNET_PQ_query_param_timestamp (&start_date),
   15381           0 :     GNUNET_PQ_query_param_timestamp (&end_date),
   15382           0 :     GNUNET_PQ_query_param_relative_time (&wad_frequency),
   15383           0 :     TALER_PQ_query_param_amount (wad_fee),
   15384           0 :     GNUNET_PQ_query_param_auto_from_type (master_sig),
   15385           0 :     GNUNET_PQ_query_param_string (partner_base_url),
   15386             :     GNUNET_PQ_query_param_end
   15387             :   };
   15388             : 
   15389           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   15390             :                                              "insert_partner",
   15391             :                                              params);
   15392             : }
   15393             : 
   15394             : 
   15395             : /**
   15396             :  * Function called to retrieve an encrypted contract.
   15397             :  *
   15398             :  * @param cls the @e cls of this struct with the plugin-specific state
   15399             :  * @param purse_pub key to lookup the contract by
   15400             :  * @param[out] pub_ckey set to the ephemeral DH used to encrypt the contract
   15401             :  * @param[out] econtract_sig set to the signature over the encrypted contract
   15402             :  * @param[out] econtract_size set to the number of bytes in @a econtract
   15403             :  * @param[out] econtract set to the encrypted contract on success, to be freed by the caller
   15404             :  * @return transaction status code
   15405             :  */
   15406             : static enum GNUNET_DB_QueryStatus
   15407           0 : postgres_select_contract (void *cls,
   15408             :                           const struct TALER_ContractDiffiePublicP *pub_ckey,
   15409             :                           struct TALER_PurseContractPublicKeyP *purse_pub,
   15410             :                           struct TALER_PurseContractSignatureP *econtract_sig,
   15411             :                           size_t *econtract_size,
   15412             :                           void **econtract)
   15413             : {
   15414           0 :   struct PostgresClosure *pg = cls;
   15415           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15416           0 :     GNUNET_PQ_query_param_auto_from_type (pub_ckey),
   15417             :     GNUNET_PQ_query_param_end
   15418             :   };
   15419           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   15420           0 :     GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
   15421             :                                           purse_pub),
   15422           0 :     GNUNET_PQ_result_spec_auto_from_type ("contract_sig",
   15423             :                                           econtract_sig),
   15424           0 :     GNUNET_PQ_result_spec_variable_size ("e_contract",
   15425             :                                          econtract,
   15426             :                                          econtract_size),
   15427             :     GNUNET_PQ_result_spec_end
   15428             :   };
   15429             : 
   15430           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   15431             :                                                    "select_contract",
   15432             :                                                    params,
   15433             :                                                    rs);
   15434             : 
   15435             : }
   15436             : 
   15437             : 
   15438             : /**
   15439             :  * Function called to retrieve an encrypted contract.
   15440             :  *
   15441             :  * @param cls the @e cls of this struct with the plugin-specific state
   15442             :  * @param purse_pub key to lookup the contract by
   15443             :  * @param[out] econtract set to the encrypted contract on success, to be freed by the caller
   15444             :  * @return transaction status code
   15445             :  */
   15446             : static enum GNUNET_DB_QueryStatus
   15447           0 : postgres_select_contract_by_purse (
   15448             :   void *cls,
   15449             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
   15450             :   struct TALER_EncryptedContract *econtract)
   15451             : {
   15452           0 :   struct PostgresClosure *pg = cls;
   15453           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15454           0 :     GNUNET_PQ_query_param_auto_from_type (purse_pub),
   15455             :     GNUNET_PQ_query_param_end
   15456             :   };
   15457           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   15458           0 :     GNUNET_PQ_result_spec_auto_from_type ("pub_ckey",
   15459             :                                           &econtract->contract_pub),
   15460           0 :     GNUNET_PQ_result_spec_auto_from_type ("contract_sig",
   15461             :                                           &econtract->econtract_sig),
   15462           0 :     GNUNET_PQ_result_spec_variable_size ("e_contract",
   15463             :                                          &econtract->econtract,
   15464             :                                          &econtract->econtract_size),
   15465             :     GNUNET_PQ_result_spec_end
   15466             :   };
   15467             : 
   15468           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   15469             :                                                    "select_contract_by_purse",
   15470             :                                                    params,
   15471             :                                                    rs);
   15472             : 
   15473             : }
   15474             : 
   15475             : 
   15476             : /**
   15477             :  * Function called to persist an encrypted contract associated with a reserve.
   15478             :  *
   15479             :  * @param cls the @e cls of this struct with the plugin-specific state
   15480             :  * @param purse_pub the purse the contract is associated with (must exist)
   15481             :  * @param econtract the encrypted contract
   15482             :  * @param[out] in_conflict set to true if @a econtract
   15483             :  *             conflicts with an existing contract;
   15484             :  *             in this case, the return value will be
   15485             :  *             #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT despite the failure
   15486             :  * @return transaction status code
   15487             :  */
   15488             : static enum GNUNET_DB_QueryStatus
   15489           0 : postgres_insert_contract (
   15490             :   void *cls,
   15491             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
   15492             :   const struct TALER_EncryptedContract *econtract,
   15493             :   bool *in_conflict)
   15494             : {
   15495           0 :   struct PostgresClosure *pg = cls;
   15496             :   enum GNUNET_DB_QueryStatus qs;
   15497           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15498           0 :     GNUNET_PQ_query_param_auto_from_type (purse_pub),
   15499           0 :     GNUNET_PQ_query_param_auto_from_type (&econtract->contract_pub),
   15500           0 :     GNUNET_PQ_query_param_fixed_size (econtract->econtract,
   15501             :                                       econtract->econtract_size),
   15502           0 :     GNUNET_PQ_query_param_auto_from_type (&econtract->econtract_sig),
   15503             :     GNUNET_PQ_query_param_end
   15504             :   };
   15505             : 
   15506           0 :   *in_conflict = false;
   15507           0 :   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
   15508             :                                            "insert_contract",
   15509             :                                            params);
   15510           0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
   15511           0 :     return qs;
   15512             :   {
   15513             :     struct TALER_EncryptedContract econtract2;
   15514             : 
   15515           0 :     qs = postgres_select_contract_by_purse (pg,
   15516             :                                             purse_pub,
   15517             :                                             &econtract2);
   15518           0 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   15519             :     {
   15520           0 :       GNUNET_break (0);
   15521           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
   15522             :     }
   15523           0 :     if ( (0 == GNUNET_memcmp (&econtract->contract_pub,
   15524           0 :                               &econtract2.contract_pub)) &&
   15525           0 :          (econtract2.econtract_size ==
   15526           0 :           econtract->econtract_size) &&
   15527           0 :          (0 == memcmp (econtract2.econtract,
   15528           0 :                        econtract->econtract,
   15529             :                        econtract->econtract_size)) )
   15530             :     {
   15531           0 :       GNUNET_free (econtract2.econtract);
   15532           0 :       return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
   15533             :     }
   15534           0 :     GNUNET_free (econtract2.econtract);
   15535           0 :     *in_conflict = true;
   15536           0 :     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   15537             :   }
   15538             : }
   15539             : 
   15540             : 
   15541             : /**
   15542             :  * Function called to return meta data about a purse by the
   15543             :  * purse public key.
   15544             :  *
   15545             :  * @param cls the @e cls of this struct with the plugin-specific state
   15546             :  * @param purse_pub public key of the purse
   15547             :  * @param[out] merge_pub public key representing the merge capability
   15548             :  * @param[out] purse_expiration when would an unmerged purse expire
   15549             :  * @param[out] h_contract_terms contract associated with the purse
   15550             :  * @param[out] age_limit the age limit for deposits into the purse
   15551             :  * @param[out] target_amount amount to be put into the purse
   15552             :  * @param[out] balance amount put so far into the purse
   15553             :  * @param[out] purse_sig signature of the purse over the initialization data
   15554             :  * @return transaction status code
   15555             :  */
   15556             : static enum GNUNET_DB_QueryStatus
   15557           0 : postgres_select_purse_request (
   15558             :   void *cls,
   15559             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
   15560             :   struct TALER_PurseMergePublicKeyP *merge_pub,
   15561             :   struct GNUNET_TIME_Timestamp *purse_expiration,
   15562             :   struct TALER_PrivateContractHashP *h_contract_terms,
   15563             :   uint32_t *age_limit,
   15564             :   struct TALER_Amount *target_amount,
   15565             :   struct TALER_Amount *balance,
   15566             :   struct TALER_PurseContractSignatureP *purse_sig)
   15567             : {
   15568           0 :   struct PostgresClosure *pg = cls;
   15569           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15570           0 :     GNUNET_PQ_query_param_auto_from_type (purse_pub),
   15571             :     GNUNET_PQ_query_param_end
   15572             :   };
   15573           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   15574           0 :     GNUNET_PQ_result_spec_auto_from_type ("merge_pub",
   15575             :                                           merge_pub),
   15576           0 :     GNUNET_PQ_result_spec_timestamp ("purse_expiration",
   15577             :                                      purse_expiration),
   15578           0 :     GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
   15579             :                                           h_contract_terms),
   15580           0 :     GNUNET_PQ_result_spec_uint32 ("age_limit",
   15581             :                                   age_limit),
   15582           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   15583             :                                  target_amount),
   15584           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
   15585             :                                  balance),
   15586           0 :     GNUNET_PQ_result_spec_auto_from_type ("purse_sig",
   15587             :                                           purse_sig),
   15588             :     GNUNET_PQ_result_spec_end
   15589             :   };
   15590           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   15591             :                                                    "select_purse_request",
   15592             :                                                    params,
   15593             :                                                    rs);
   15594             : }
   15595             : 
   15596             : 
   15597             : /**
   15598             :  * Function called to create a new purse with certain meta data.
   15599             :  *
   15600             :  * @param cls the @e cls of this struct with the plugin-specific state
   15601             :  * @param purse_pub public key of the new purse
   15602             :  * @param merge_pub public key providing the merge capability
   15603             :  * @param purse_expiration time when the purse will expire
   15604             :  * @param h_contract_terms hash of the contract for the purse
   15605             :  * @param age_limit age limit to enforce for payments into the purse
   15606             :  * @param flags flags for the operation
   15607             :  * @param purse_fee fee we are allowed to charge to the reserve (depending on @a flags)
   15608             :  * @param amount target amount (with fees) to be put into the purse
   15609             :  * @param purse_sig signature with @a purse_pub's private key affirming the above
   15610             :  * @param[out] in_conflict set to true if the meta data
   15611             :  *             conflicts with an existing purse;
   15612             :  *             in this case, the return value will be
   15613             :  *             #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT despite the failure
   15614             :  * @return transaction status code
   15615             :  */
   15616             : static enum GNUNET_DB_QueryStatus
   15617           0 : postgres_insert_purse_request (
   15618             :   void *cls,
   15619             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
   15620             :   const struct TALER_PurseMergePublicKeyP *merge_pub,
   15621             :   struct GNUNET_TIME_Timestamp purse_expiration,
   15622             :   const struct TALER_PrivateContractHashP *h_contract_terms,
   15623             :   uint32_t age_limit,
   15624             :   enum TALER_WalletAccountMergeFlags flags,
   15625             :   const struct TALER_Amount *purse_fee,
   15626             :   const struct TALER_Amount *amount,
   15627             :   const struct TALER_PurseContractSignatureP *purse_sig,
   15628             :   bool *in_conflict)
   15629             : {
   15630           0 :   struct PostgresClosure *pg = cls;
   15631             :   enum GNUNET_DB_QueryStatus qs;
   15632           0 :   struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
   15633           0 :   uint32_t flags32 = (uint32_t) flags;
   15634           0 :   bool in_reserve_quota = (TALER_WAMF_MODE_CREATE_FROM_PURSE_QUOTA
   15635           0 :                            == (flags & TALER_WAMF_MERGE_MODE_MASK));
   15636           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15637           0 :     GNUNET_PQ_query_param_auto_from_type (purse_pub),
   15638           0 :     GNUNET_PQ_query_param_auto_from_type (merge_pub),
   15639           0 :     GNUNET_PQ_query_param_timestamp (&now),
   15640           0 :     GNUNET_PQ_query_param_timestamp (&purse_expiration),
   15641           0 :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
   15642           0 :     GNUNET_PQ_query_param_uint32 (&age_limit),
   15643           0 :     GNUNET_PQ_query_param_uint32 (&flags32),
   15644           0 :     GNUNET_PQ_query_param_bool (in_reserve_quota),
   15645           0 :     TALER_PQ_query_param_amount (amount),
   15646           0 :     TALER_PQ_query_param_amount (purse_fee),
   15647           0 :     GNUNET_PQ_query_param_auto_from_type (purse_sig),
   15648             :     GNUNET_PQ_query_param_end
   15649             :   };
   15650             : 
   15651           0 :   *in_conflict = false;
   15652           0 :   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
   15653             :                                            "insert_purse_request",
   15654             :                                            params);
   15655           0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
   15656           0 :     return qs;
   15657             :   {
   15658             :     struct TALER_PurseMergePublicKeyP merge_pub2;
   15659             :     struct GNUNET_TIME_Timestamp purse_expiration2;
   15660             :     struct TALER_PrivateContractHashP h_contract_terms2;
   15661             :     uint32_t age_limit2;
   15662             :     struct TALER_Amount amount2;
   15663             :     struct TALER_Amount balance;
   15664             :     struct TALER_PurseContractSignatureP purse_sig2;
   15665             : 
   15666           0 :     qs = postgres_select_purse_request (pg,
   15667             :                                         purse_pub,
   15668             :                                         &merge_pub2,
   15669             :                                         &purse_expiration2,
   15670             :                                         &h_contract_terms2,
   15671             :                                         &age_limit2,
   15672             :                                         &amount2,
   15673             :                                         &balance,
   15674             :                                         &purse_sig2);
   15675           0 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   15676             :     {
   15677           0 :       GNUNET_break (0);
   15678           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
   15679             :     }
   15680           0 :     if ( (age_limit2 == age_limit) &&
   15681           0 :          (0 == TALER_amount_cmp (amount,
   15682           0 :                                  &amount2)) &&
   15683           0 :          (0 == GNUNET_memcmp (&h_contract_terms2,
   15684           0 :                               h_contract_terms)) &&
   15685           0 :          (0 == GNUNET_memcmp (&merge_pub2,
   15686             :                               merge_pub)) )
   15687             :     {
   15688           0 :       return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
   15689             :     }
   15690           0 :     *in_conflict = true;
   15691           0 :     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   15692             :   }
   15693             : }
   15694             : 
   15695             : 
   15696             : /**
   15697             :  * Function called to clean up one expired purse.
   15698             :  *
   15699             :  * @param cls the @e cls of this struct with the plugin-specific state
   15700             :  * @param start_time select purse expired after this time
   15701             :  * @param end_time select purse expired before this time
   15702             :  * @return transaction status code (#GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if no purse expired in the given time interval).
   15703             :  */
   15704             : static enum GNUNET_DB_QueryStatus
   15705           0 : postgres_expire_purse (
   15706             :   void *cls,
   15707             :   struct GNUNET_TIME_Absolute start_time,
   15708             :   struct GNUNET_TIME_Absolute end_time)
   15709             : {
   15710           0 :   struct PostgresClosure *pg = cls;
   15711           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15712           0 :     GNUNET_PQ_query_param_absolute_time (&start_time),
   15713           0 :     GNUNET_PQ_query_param_absolute_time (&end_time),
   15714             :     GNUNET_PQ_query_param_end
   15715             :   };
   15716           0 :   bool found = false;
   15717           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   15718           0 :     GNUNET_PQ_result_spec_bool ("found",
   15719             :                                 &found),
   15720             :     GNUNET_PQ_result_spec_end
   15721             :   };
   15722             :   enum GNUNET_DB_QueryStatus qs;
   15723             : 
   15724           0 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   15725             :                                                  "call_expire_purse",
   15726             :                                                  params,
   15727             :                                                  rs);
   15728           0 :   if (qs < 0)
   15729           0 :     return qs;
   15730           0 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
   15731             :   return found
   15732             :          ? GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
   15733           0 :          : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
   15734             : }
   15735             : 
   15736             : 
   15737             : /**
   15738             :  * Function called to obtain information about a purse.
   15739             :  *
   15740             :  * @param cls the @e cls of this struct with the plugin-specific state
   15741             :  * @param purse_pub public key of the new purse
   15742             :  * @param[out] purse_expiration set to time when the purse will expire
   15743             :  * @param[out] amount set to target amount (with fees) to be put into the purse
   15744             :  * @param[out] deposited set to actual amount put into the purse so far
   15745             :  * @param[out] h_contract_terms set to hash of the contract for the purse
   15746             :  * @param[out] merge_timestamp set to time when the purse was merged, or NEVER if not
   15747             :  * @return transaction status code
   15748             :  */
   15749             : static enum GNUNET_DB_QueryStatus
   15750           0 : postgres_select_purse (
   15751             :   void *cls,
   15752             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
   15753             :   struct GNUNET_TIME_Timestamp *purse_expiration,
   15754             :   struct TALER_Amount *amount,
   15755             :   struct TALER_Amount *deposited,
   15756             :   struct TALER_PrivateContractHashP *h_contract_terms,
   15757             :   struct GNUNET_TIME_Timestamp *merge_timestamp)
   15758             : {
   15759           0 :   struct PostgresClosure *pg = cls;
   15760           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15761           0 :     GNUNET_PQ_query_param_auto_from_type (purse_pub),
   15762             :     GNUNET_PQ_query_param_end
   15763             :   };
   15764           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   15765           0 :     GNUNET_PQ_result_spec_timestamp ("purse_expiration",
   15766             :                                      purse_expiration),
   15767           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   15768             :                                  amount),
   15769           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
   15770             :                                  deposited),
   15771           0 :     GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
   15772             :                                           h_contract_terms),
   15773           0 :     GNUNET_PQ_result_spec_allow_null (
   15774             :       GNUNET_PQ_result_spec_timestamp ("merge_timestamp",
   15775             :                                        merge_timestamp),
   15776             :       NULL),
   15777             :     GNUNET_PQ_result_spec_end
   15778             :   };
   15779             : 
   15780           0 :   *merge_timestamp = GNUNET_TIME_UNIT_FOREVER_TS;
   15781           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   15782             :                                                    "select_purse",
   15783             :                                                    params,
   15784             :                                                    rs);
   15785             : }
   15786             : 
   15787             : 
   15788             : /**
   15789             :  * Function called to return meta data about a purse by the
   15790             :  * merge capability key.
   15791             :  *
   15792             :  * @param cls the @e cls of this struct with the plugin-specific state
   15793             :  * @param merge_pub public key representing the merge capability
   15794             :  * @param[out] purse_pub public key of the purse
   15795             :  * @param[out] purse_expiration when would an unmerged purse expire
   15796             :  * @param[out] h_contract_terms contract associated with the purse
   15797             :  * @param[out] age_limit the age limit for deposits into the purse
   15798             :  * @param[out] target_amount amount to be put into the purse
   15799             :  * @param[out] balance amount put so far into the purse
   15800             :  * @param[out] purse_sig signature of the purse over the initialization data
   15801             :  * @return transaction status code
   15802             :  */
   15803             : static enum GNUNET_DB_QueryStatus
   15804           0 : postgres_select_purse_by_merge_pub (
   15805             :   void *cls,
   15806             :   const struct TALER_PurseMergePublicKeyP *merge_pub,
   15807             :   struct TALER_PurseContractPublicKeyP *purse_pub,
   15808             :   struct GNUNET_TIME_Timestamp *purse_expiration,
   15809             :   struct TALER_PrivateContractHashP *h_contract_terms,
   15810             :   uint32_t *age_limit,
   15811             :   struct TALER_Amount *target_amount,
   15812             :   struct TALER_Amount *balance,
   15813             :   struct TALER_PurseContractSignatureP *purse_sig)
   15814             : {
   15815           0 :   struct PostgresClosure *pg = cls;
   15816           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15817           0 :     GNUNET_PQ_query_param_auto_from_type (merge_pub),
   15818             :     GNUNET_PQ_query_param_end
   15819             :   };
   15820           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   15821           0 :     GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
   15822             :                                           purse_pub),
   15823           0 :     GNUNET_PQ_result_spec_timestamp ("purse_expiration",
   15824             :                                      purse_expiration),
   15825           0 :     GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
   15826             :                                           h_contract_terms),
   15827           0 :     GNUNET_PQ_result_spec_uint32 ("age_limit",
   15828             :                                   age_limit),
   15829           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   15830             :                                  target_amount),
   15831           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
   15832             :                                  balance),
   15833           0 :     GNUNET_PQ_result_spec_auto_from_type ("purse_sig",
   15834             :                                           purse_sig),
   15835             :     GNUNET_PQ_result_spec_end
   15836             :   };
   15837           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   15838             :                                                    "select_purse_by_merge_pub",
   15839             :                                                    params,
   15840             :                                                    rs);
   15841             : }
   15842             : 
   15843             : 
   15844             : /**
   15845             :  * Function called to execute a transaction crediting
   15846             :  * a purse with @a amount from @a coin_pub. Reduces the
   15847             :  * value of @a coin_pub and increase the balance of
   15848             :  * the @a purse_pub purse. If the balance reaches the
   15849             :  * target amount and the purse has been merged, triggers
   15850             :  * the updates of the reserve/account balance.
   15851             :  *
   15852             :  * @param cls the @e cls of this struct with the plugin-specific state
   15853             :  * @param purse_pub purse to credit
   15854             :  * @param coin_pub coin to deposit (debit)
   15855             :  * @param amount fraction of the coin's value to deposit
   15856             :  * @param coin_sig signature affirming the operation
   15857             :  * @param amount_minus_fee amount to add to the purse
   15858             :  * @param[out] balance_ok set to false if the coin's
   15859             :  *        remaining balance is below @a amount;
   15860             :  *             in this case, the return value will be
   15861             :  *             #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT despite the failure
   15862             :  * @param[out] conflict set to true if the deposit failed due to a conflict (coin already spent,
   15863             :  *             or deposited into this purse with a different amount)
   15864             :  * @return transaction status code
   15865             :  */
   15866             : static enum GNUNET_DB_QueryStatus
   15867           0 : postgres_do_purse_deposit (
   15868             :   void *cls,
   15869             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
   15870             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
   15871             :   const struct TALER_Amount *amount,
   15872             :   const struct TALER_CoinSpendSignatureP *coin_sig,
   15873             :   const struct TALER_Amount *amount_minus_fee,
   15874             :   bool *balance_ok,
   15875             :   bool *conflict)
   15876             : {
   15877           0 :   struct PostgresClosure *pg = cls;
   15878             :   struct GNUNET_TIME_Timestamp reserve_expiration;
   15879           0 :   uint64_t partner_id = 0; /* FIXME #7271: WAD support... */
   15880           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15881           0 :     GNUNET_PQ_query_param_uint64 (&partner_id),
   15882           0 :     GNUNET_PQ_query_param_auto_from_type (purse_pub),
   15883           0 :     TALER_PQ_query_param_amount (amount),
   15884           0 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
   15885           0 :     GNUNET_PQ_query_param_auto_from_type (coin_sig),
   15886           0 :     TALER_PQ_query_param_amount (amount_minus_fee),
   15887           0 :     GNUNET_PQ_query_param_timestamp (&reserve_expiration),
   15888             :     GNUNET_PQ_query_param_end
   15889             :   };
   15890           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   15891           0 :     GNUNET_PQ_result_spec_bool ("balance_ok",
   15892             :                                 balance_ok),
   15893           0 :     GNUNET_PQ_result_spec_bool ("conflict",
   15894             :                                 conflict),
   15895             :     GNUNET_PQ_result_spec_end
   15896             :   };
   15897             : 
   15898             :   reserve_expiration
   15899           0 :     = GNUNET_TIME_absolute_to_timestamp (
   15900             :         GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
   15901             :                                   pg->legal_reserve_expiration_time));
   15902           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   15903             :                                                    "call_purse_deposit",
   15904             :                                                    params,
   15905             :                                                    rs);
   15906             : }
   15907             : 
   15908             : 
   15909             : /**
   15910             :  * Set the current @a balance in the purse
   15911             :  * identified by @a purse_pub. Used by the auditor
   15912             :  * to update the balance as calculated by the auditor.
   15913             :  *
   15914             :  * @param cls closure
   15915             :  * @param purse_pub public key of a purse
   15916             :  * @param balance new balance to store under the purse
   15917             :  * @return transaction status
   15918             :  */
   15919             : static enum GNUNET_DB_QueryStatus
   15920           0 : postgres_set_purse_balance (
   15921             :   void *cls,
   15922             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
   15923             :   const struct TALER_Amount *balance)
   15924             : {
   15925           0 :   struct PostgresClosure *pg = cls;
   15926           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15927           0 :     GNUNET_PQ_query_param_auto_from_type (purse_pub),
   15928           0 :     TALER_PQ_query_param_amount (balance),
   15929             :     GNUNET_PQ_query_param_end
   15930             :   };
   15931             : 
   15932           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   15933             :                                              "set_purse_balance",
   15934             :                                              params);
   15935             : }
   15936             : 
   15937             : 
   15938             : /**
   15939             :  * Function called to obtain a coin deposit data from
   15940             :  * depositing the coin into a purse.
   15941             :  *
   15942             :  * @param cls the @e cls of this struct with the plugin-specific state
   15943             :  * @param purse_pub purse to credit
   15944             :  * @param coin_pub coin to deposit (debit)
   15945             :  * @param[out] amount set fraction of the coin's value that was deposited (with fee)
   15946             :  * @param[out] h_denom_pub set to hash of denomination of the coin
   15947             :  * @param[out] phac set to hash of age restriction on the coin
   15948             :  * @param[out] coin_sig set to signature affirming the operation
   15949             :  * @param[out] partner_url set to the URL of the partner exchange, or NULL for ourselves, must be freed by caller
   15950             :  * @return transaction status code
   15951             :  */
   15952             : static enum GNUNET_DB_QueryStatus
   15953           0 : postgres_get_purse_deposit (
   15954             :   void *cls,
   15955             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
   15956             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
   15957             :   struct TALER_Amount *amount,
   15958             :   struct TALER_DenominationHashP *h_denom_pub,
   15959             :   struct TALER_AgeCommitmentHash *phac,
   15960             :   struct TALER_CoinSpendSignatureP *coin_sig,
   15961             :   char **partner_url)
   15962             : {
   15963           0 :   struct PostgresClosure *pg = cls;
   15964           0 :   struct GNUNET_PQ_QueryParam params[] = {
   15965           0 :     GNUNET_PQ_query_param_auto_from_type (purse_pub),
   15966           0 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
   15967             :     GNUNET_PQ_query_param_end
   15968             :   };
   15969             :   bool is_null;
   15970           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   15971           0 :     GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
   15972             :                                           h_denom_pub),
   15973           0 :     GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
   15974             :                                           phac),
   15975           0 :     GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
   15976             :                                           coin_sig),
   15977           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
   15978             :                                  amount),
   15979           0 :     GNUNET_PQ_result_spec_allow_null (
   15980             :       GNUNET_PQ_result_spec_string ("partner_base_url",
   15981             :                                     partner_url),
   15982             :       &is_null),
   15983             :     GNUNET_PQ_result_spec_end
   15984             :   };
   15985             : 
   15986           0 :   *partner_url = NULL;
   15987           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   15988             :                                                    "select_purse_deposit_by_coin_pub",
   15989             :                                                    params,
   15990             :                                                    rs);
   15991             : }
   15992             : 
   15993             : 
   15994             : /**
   15995             :  * Function called to approve merging a purse into a
   15996             :  * reserve by the respective purse merge key.
   15997             :  *
   15998             :  * @param cls the @e cls of this struct with the plugin-specific state
   15999             :  * @param purse_pub purse to merge
   16000             :  * @param merge_sig signature affirming the merge
   16001             :  * @param merge_timestamp time of the merge
   16002             :  * @param reserve_sig signature of the reserve affirming the merge
   16003             :  * @param partner_url URL of the partner exchange, can be NULL if the reserves lives with us
   16004             :  * @param reserve_pub public key of the reserve to credit
   16005             :  * @param[out] no_partner set to true if @a partner_url is unknown
   16006             :  * @param[out] no_balance set to true if the @a purse_pub is not paid up yet
   16007             :  * @param[out] in_conflict set to true if @a purse_pub was merged into a different reserve already
   16008             :   * @return transaction status code
   16009             :  */
   16010             : static enum GNUNET_DB_QueryStatus
   16011           0 : postgres_do_purse_merge (
   16012             :   void *cls,
   16013             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
   16014             :   const struct TALER_PurseMergeSignatureP *merge_sig,
   16015             :   const struct GNUNET_TIME_Timestamp merge_timestamp,
   16016             :   const struct TALER_ReserveSignatureP *reserve_sig,
   16017             :   const char *partner_url,
   16018             :   const struct TALER_ReservePublicKeyP *reserve_pub,
   16019             :   bool *no_partner,
   16020             :   bool *no_balance,
   16021             :   bool *in_conflict)
   16022             : {
   16023           0 :   struct PostgresClosure *pg = cls;
   16024             :   struct TALER_PaytoHashP h_payto;
   16025             :   struct GNUNET_TIME_Timestamp expiration
   16026           0 :     = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_YEARS); /* FIXME: make this configurable? */
   16027           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16028           0 :     GNUNET_PQ_query_param_auto_from_type (purse_pub),
   16029           0 :     GNUNET_PQ_query_param_auto_from_type (merge_sig),
   16030           0 :     GNUNET_PQ_query_param_timestamp (&merge_timestamp),
   16031           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_sig),
   16032             :     (NULL == partner_url)
   16033           0 :     ? GNUNET_PQ_query_param_null ()
   16034           0 :     : GNUNET_PQ_query_param_string (partner_url),
   16035           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
   16036           0 :     GNUNET_PQ_query_param_auto_from_type (&h_payto),
   16037           0 :     GNUNET_PQ_query_param_timestamp (&expiration),
   16038             :     GNUNET_PQ_query_param_end
   16039             :   };
   16040           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   16041           0 :     GNUNET_PQ_result_spec_bool ("no_partner",
   16042             :                                 no_partner),
   16043           0 :     GNUNET_PQ_result_spec_bool ("no_balance",
   16044             :                                 no_balance),
   16045           0 :     GNUNET_PQ_result_spec_bool ("conflict",
   16046             :                                 in_conflict),
   16047             :     GNUNET_PQ_result_spec_end
   16048             :   };
   16049             : 
   16050             :   {
   16051             :     char *payto_uri;
   16052             : 
   16053           0 :     payto_uri = TALER_reserve_make_payto (pg->exchange_url,
   16054             :                                           reserve_pub);
   16055           0 :     TALER_payto_hash (payto_uri,
   16056             :                       &h_payto);
   16057           0 :     GNUNET_free (payto_uri);
   16058             :   }
   16059           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   16060             :                                                    "call_purse_merge",
   16061             :                                                    params,
   16062             :                                                    rs);
   16063             : }
   16064             : 
   16065             : 
   16066             : /**
   16067             :  * Function called insert request to merge a purse into a reserve by the
   16068             :  * respective purse merge key. The purse must not have been merged into a
   16069             :  * different reserve.
   16070             :  *
   16071             :  * @param cls the @e cls of this struct with the plugin-specific state
   16072             :  * @param purse_pub purse to merge
   16073             :  * @param merge_sig signature affirming the merge
   16074             :  * @param merge_timestamp time of the merge
   16075             :  * @param reserve_sig signature of the reserve affirming the merge
   16076             :  * @param purse_fee amount to charge the reserve for the purse creation, NULL to use the quota
   16077             :  * @param reserve_pub public key of the reserve to credit
   16078             :  * @param[out] in_conflict set to true if @a purse_pub was merged into a different reserve already
   16079             :  * @param[out] no_reserve set to true if @a reserve_pub is not a known reserve
   16080             :  * @param[out] insufficient_funds set to true if @a reserve_pub has insufficient capacity to create another purse
   16081             :  * @return transaction status code
   16082             :  */
   16083             : static enum GNUNET_DB_QueryStatus
   16084           0 : postgres_do_reserve_purse (
   16085             :   void *cls,
   16086             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
   16087             :   const struct TALER_PurseMergeSignatureP *merge_sig,
   16088             :   const struct GNUNET_TIME_Timestamp merge_timestamp,
   16089             :   const struct TALER_ReserveSignatureP *reserve_sig,
   16090             :   const struct TALER_Amount *purse_fee,
   16091             :   const struct TALER_ReservePublicKeyP *reserve_pub,
   16092             :   bool *in_conflict,
   16093             :   bool *no_reserve,
   16094             :   bool *insufficient_funds)
   16095             : {
   16096           0 :   struct PostgresClosure *pg = cls;
   16097             :   struct TALER_Amount zero_fee;
   16098             :   struct TALER_PaytoHashP h_payto;
   16099           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16100           0 :     GNUNET_PQ_query_param_auto_from_type (purse_pub),
   16101           0 :     GNUNET_PQ_query_param_auto_from_type (merge_sig),
   16102           0 :     GNUNET_PQ_query_param_timestamp (&merge_timestamp),
   16103           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_sig),
   16104           0 :     GNUNET_PQ_query_param_bool (NULL == purse_fee),
   16105           0 :     TALER_PQ_query_param_amount (NULL == purse_fee
   16106             :                                  ? &zero_fee
   16107             :                                  : purse_fee),
   16108           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
   16109           0 :     GNUNET_PQ_query_param_auto_from_type (&h_payto),
   16110             :     GNUNET_PQ_query_param_end
   16111             :   };
   16112           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   16113           0 :     GNUNET_PQ_result_spec_bool ("insufficient_funds",
   16114             :                                 insufficient_funds),
   16115           0 :     GNUNET_PQ_result_spec_bool ("conflict",
   16116             :                                 in_conflict),
   16117           0 :     GNUNET_PQ_result_spec_bool ("no_reserve",
   16118             :                                 no_reserve),
   16119             :     GNUNET_PQ_result_spec_end
   16120             :   };
   16121             : 
   16122             :   {
   16123             :     char *payto_uri;
   16124             : 
   16125           0 :     payto_uri = TALER_reserve_make_payto (pg->exchange_url,
   16126             :                                           reserve_pub);
   16127           0 :     TALER_payto_hash (payto_uri,
   16128             :                       &h_payto);
   16129           0 :     GNUNET_free (payto_uri);
   16130             :   }
   16131           0 :   GNUNET_assert (GNUNET_OK ==
   16132             :                  TALER_amount_set_zero (pg->currency,
   16133             :                                         &zero_fee));
   16134           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   16135             :                                                    "call_reserve_purse",
   16136             :                                                    params,
   16137             :                                                    rs);
   16138             : }
   16139             : 
   16140             : 
   16141             : /**
   16142             :  * Function called to approve merging of a purse with
   16143             :  * an account, made by the receiving account.
   16144             :  *
   16145             :  * @param cls the @e cls of this struct with the plugin-specific state
   16146             :  * @param purse_pub public key of the purse
   16147             :  * @param[out] merge_sig set to the signature confirming the merge
   16148             :  * @param[out] merge_timestamp set to the time of the merge
   16149             :  * @param[out] partner_url set to the URL of the target exchange, or NULL if the target exchange is us. To be freed by the caller.
   16150             :  * @param[out] reserve_pub set to the public key of the reserve/account being credited
   16151             :  * @return transaction status code
   16152             :  */
   16153             : static enum GNUNET_DB_QueryStatus
   16154           0 : postgres_select_purse_merge (
   16155             :   void *cls,
   16156             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
   16157             :   struct TALER_PurseMergeSignatureP *merge_sig,
   16158             :   struct GNUNET_TIME_Timestamp *merge_timestamp,
   16159             :   char **partner_url,
   16160             :   struct TALER_ReservePublicKeyP *reserve_pub)
   16161             : {
   16162           0 :   struct PostgresClosure *pg = cls;
   16163           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16164           0 :     GNUNET_PQ_query_param_auto_from_type (purse_pub),
   16165             :     GNUNET_PQ_query_param_end
   16166             :   };
   16167             :   bool is_null;
   16168           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   16169           0 :     GNUNET_PQ_result_spec_auto_from_type ("merge_sig",
   16170             :                                           merge_sig),
   16171           0 :     GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
   16172             :                                           reserve_pub),
   16173           0 :     GNUNET_PQ_result_spec_timestamp ("merge_timestamp",
   16174             :                                      merge_timestamp),
   16175           0 :     GNUNET_PQ_result_spec_allow_null (
   16176             :       GNUNET_PQ_result_spec_string ("partner_base_url",
   16177             :                                     partner_url),
   16178             :       &is_null),
   16179             :     GNUNET_PQ_result_spec_end
   16180             :   };
   16181             : 
   16182           0 :   *partner_url = NULL;
   16183           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   16184             :                                                    "select_purse_merge",
   16185             :                                                    params,
   16186             :                                                    rs);
   16187             : }
   16188             : 
   16189             : 
   16190             : /**
   16191             :  * Function called to persist a signature that
   16192             :  * prove that the client requested an
   16193             :  * account history.  Debits the @a history_fee from
   16194             :  * the reserve (if possible).
   16195             :  *
   16196             :  * @param cls the @e cls of this struct with the plugin-specific state
   16197             :  * @param reserve_pub account that the history was requested for
   16198             :  * @param reserve_sig signature affirming the request
   16199             :  * @param request_timestamp when was the request made
   16200             :  * @param history_fee how much should the @a reserve_pub be charged for the request
   16201             :  * @param[out] balance_ok set to TRUE if the reserve balance
   16202             :  *         was sufficient
   16203             :  * @param[out] idempotent set to TRUE if the request is already in the DB
   16204             :  * @return transaction status code
   16205             :  */
   16206             : static enum GNUNET_DB_QueryStatus
   16207           0 : postgres_insert_history_request (
   16208             :   void *cls,
   16209             :   const struct TALER_ReservePublicKeyP *reserve_pub,
   16210             :   const struct TALER_ReserveSignatureP *reserve_sig,
   16211             :   struct GNUNET_TIME_Timestamp request_timestamp,
   16212             :   const struct TALER_Amount *history_fee,
   16213             :   bool *balance_ok,
   16214             :   bool *idempotent)
   16215             : {
   16216           0 :   struct PostgresClosure *pg = cls;
   16217           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16218           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
   16219           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_sig),
   16220           0 :     GNUNET_PQ_query_param_timestamp (&request_timestamp),
   16221           0 :     TALER_PQ_query_param_amount (history_fee),
   16222             :     GNUNET_PQ_query_param_end
   16223             :   };
   16224           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   16225           0 :     GNUNET_PQ_result_spec_bool ("balance_ok",
   16226             :                                 balance_ok),
   16227           0 :     GNUNET_PQ_result_spec_bool ("idempotent",
   16228             :                                 idempotent),
   16229             :     GNUNET_PQ_result_spec_end
   16230             :   };
   16231             : 
   16232           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   16233             :                                                    "call_history_request",
   16234             :                                                    params,
   16235             :                                                    rs);
   16236             : }
   16237             : 
   16238             : 
   16239             : /**
   16240             :  * Function called to initiate closure of an account.
   16241             :  *
   16242             :  * @param cls the @e cls of this struct with the plugin-specific state
   16243             :  * @param reserve_pub public key of the account to close
   16244             :  * @param reserve_sig signature affiming that the account is to be closed
   16245             :  * @param request_timestamp time of the close request (client-side?)
   16246             :  * @param[out] final_balance set to the final balance in the account that will be wired back to the origin account
   16247             :  * @return transaction status code
   16248             :  */
   16249             : static enum GNUNET_DB_QueryStatus
   16250           0 : postgres_insert_close_request (
   16251             :   void *cls,
   16252             :   const struct TALER_ReservePublicKeyP *reserve_pub,
   16253             :   const struct TALER_ReserveSignatureP *reserve_sig,
   16254             :   struct GNUNET_TIME_Timestamp request_timestamp,
   16255             :   struct TALER_Amount *final_balance)
   16256             : {
   16257           0 :   struct PostgresClosure *pg = cls;
   16258           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16259           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
   16260           0 :     GNUNET_PQ_query_param_timestamp (&request_timestamp),
   16261           0 :     GNUNET_PQ_query_param_auto_from_type (reserve_sig),
   16262             :     GNUNET_PQ_query_param_end
   16263             :   };
   16264           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   16265           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("out_final_balance",
   16266             :                                  final_balance),
   16267             :     GNUNET_PQ_result_spec_end
   16268             :   };
   16269             : 
   16270           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   16271             :                                                    "call_account_close",
   16272             :                                                    params,
   16273             :                                                    rs);
   16274             : }
   16275             : 
   16276             : 
   16277             : /**
   16278             :  * Function called to persist a request to drain profits.
   16279             :  *
   16280             :  * @param cls the @e cls of this struct with the plugin-specific state
   16281             :  * @param wtid wire transfer ID to use
   16282             :  * @param account_section account to drain
   16283             :  * @param payto_uri account to wire funds to
   16284             :  * @param request_timestamp when was the request made
   16285             :  * @param amount amount to wire
   16286             :  * @param master_sig signature affirming the operation
   16287             :  * @return transaction status code
   16288             :  */
   16289             : static enum GNUNET_DB_QueryStatus
   16290           0 : postgres_insert_drain_profit (
   16291             :   void *cls,
   16292             :   const struct TALER_WireTransferIdentifierRawP *wtid,
   16293             :   const char *account_section,
   16294             :   const char *payto_uri,
   16295             :   struct GNUNET_TIME_Timestamp request_timestamp,
   16296             :   const struct TALER_Amount *amount,
   16297             :   const struct TALER_MasterSignatureP *master_sig)
   16298             : {
   16299           0 :   struct PostgresClosure *pg = cls;
   16300           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16301           0 :     GNUNET_PQ_query_param_auto_from_type (wtid),
   16302           0 :     GNUNET_PQ_query_param_string (account_section),
   16303           0 :     GNUNET_PQ_query_param_string (payto_uri),
   16304           0 :     GNUNET_PQ_query_param_timestamp (&request_timestamp),
   16305           0 :     TALER_PQ_query_param_amount (amount),
   16306           0 :     GNUNET_PQ_query_param_auto_from_type (master_sig),
   16307             :     GNUNET_PQ_query_param_end
   16308             :   };
   16309             : 
   16310           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   16311             :                                              "drain_profit_insert",
   16312             :                                              params);
   16313             : }
   16314             : 
   16315             : 
   16316             : /**
   16317             :  * Get profit drain operation ready to execute.
   16318             :  *
   16319             :  * @param cls the @e cls of this struct with the plugin-specific state
   16320             :  * @param[out] serial set to serial ID of the entry
   16321             :  * @param[out] wtid set set to wire transfer ID to use
   16322             :  * @param[out] account_section set to  account to drain
   16323             :  * @param[out] payto_uri set to account to wire funds to
   16324             :  * @param[out] request_timestamp set to time of the signature
   16325             :  * @param[out] amount set to amount to wire
   16326             :  * @param[out] master_sig set to signature affirming the operation
   16327             :  * @return transaction status code
   16328             :  */
   16329             : static enum GNUNET_DB_QueryStatus
   16330           0 : postgres_profit_drains_get_pending (
   16331             :   void *cls,
   16332             :   uint64_t *serial,
   16333             :   struct TALER_WireTransferIdentifierRawP *wtid,
   16334             :   char **account_section,
   16335             :   char **payto_uri,
   16336             :   struct GNUNET_TIME_Timestamp *request_timestamp,
   16337             :   struct TALER_Amount *amount,
   16338             :   struct TALER_MasterSignatureP *master_sig)
   16339             : {
   16340           0 :   struct PostgresClosure *pg = cls;
   16341           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16342             :     GNUNET_PQ_query_param_end
   16343             :   };
   16344           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   16345           0 :     GNUNET_PQ_result_spec_uint64 ("profit_drain_serial_id",
   16346             :                                   serial),
   16347           0 :     GNUNET_PQ_result_spec_auto_from_type ("wtid",
   16348             :                                           wtid),
   16349           0 :     GNUNET_PQ_result_spec_string ("account_section",
   16350             :                                   account_section),
   16351           0 :     GNUNET_PQ_result_spec_string ("payto_uri",
   16352             :                                   payto_uri),
   16353           0 :     GNUNET_PQ_result_spec_timestamp ("trigger_date",
   16354             :                                      request_timestamp),
   16355           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
   16356             :                                  amount),
   16357           0 :     GNUNET_PQ_result_spec_auto_from_type ("master_sig",
   16358             :                                           master_sig),
   16359             :     GNUNET_PQ_result_spec_end
   16360             :   };
   16361             : 
   16362           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   16363             :                                                    "get_ready_profit_drain",
   16364             :                                                    params,
   16365             :                                                    rs);
   16366             : }
   16367             : 
   16368             : 
   16369             : /**
   16370             :  * Function called to get information about a profit drain event.
   16371             :  *
   16372             :  * @param cls the @e cls of this struct with the plugin-specific state
   16373             :  * @param wtid wire transfer ID to look up drain event for
   16374             :  * @param[out] serial set to serial ID of the entry
   16375             :  * @param[out] account_section set to account to drain
   16376             :  * @param[out] payto_uri set to account to wire funds to
   16377             :  * @param[out] request_timestamp set to time of the signature
   16378             :  * @param[out] amount set to amount to wire
   16379             :  * @param[out] master_sig set to signature affirming the operation
   16380             :  * @return transaction status code
   16381             :  */
   16382             : static enum GNUNET_DB_QueryStatus
   16383           0 : postgres_get_drain_profit (
   16384             :   void *cls,
   16385             :   const struct TALER_WireTransferIdentifierRawP *wtid,
   16386             :   uint64_t *serial,
   16387             :   char **account_section,
   16388             :   char **payto_uri,
   16389             :   struct GNUNET_TIME_Timestamp *request_timestamp,
   16390             :   struct TALER_Amount *amount,
   16391             :   struct TALER_MasterSignatureP *master_sig)
   16392             : {
   16393           0 :   struct PostgresClosure *pg = cls;
   16394           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16395           0 :     GNUNET_PQ_query_param_auto_from_type (wtid),
   16396             :     GNUNET_PQ_query_param_end
   16397             :   };
   16398           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   16399           0 :     GNUNET_PQ_result_spec_uint64 ("profit_drain_serial_id",
   16400             :                                   serial),
   16401           0 :     GNUNET_PQ_result_spec_string ("account_section",
   16402             :                                   account_section),
   16403           0 :     GNUNET_PQ_result_spec_string ("payto_uri",
   16404             :                                   payto_uri),
   16405           0 :     GNUNET_PQ_result_spec_timestamp ("trigger_date",
   16406             :                                      request_timestamp),
   16407           0 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
   16408             :                                  amount),
   16409           0 :     GNUNET_PQ_result_spec_auto_from_type ("master_sig",
   16410             :                                           master_sig),
   16411             :     GNUNET_PQ_result_spec_end
   16412             :   };
   16413             : 
   16414           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   16415             :                                                    "get_profit_drain",
   16416             :                                                    params,
   16417             :                                                    rs);
   16418             : }
   16419             : 
   16420             : 
   16421             : /**
   16422             :  * Set profit drain operation to finished.
   16423             :  *
   16424             :  * @param cls the @e cls of this struct with the plugin-specific state
   16425             :  * @param serial serial ID of the entry to mark finished
   16426             :  * @return transaction status code
   16427             :  */
   16428             : static enum GNUNET_DB_QueryStatus
   16429           0 : postgres_profit_drains_set_finished (
   16430             :   void *cls,
   16431             :   uint64_t serial)
   16432             : {
   16433           0 :   struct PostgresClosure *pg = cls;
   16434           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16435           0 :     GNUNET_PQ_query_param_uint64 (&serial),
   16436             :     GNUNET_PQ_query_param_end
   16437             :   };
   16438             : 
   16439           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   16440             :                                              "drain_profit_set_finished",
   16441             :                                              params);
   16442             : }
   16443             : 
   16444             : 
   16445             : /**
   16446             :  * Insert KYC requirement for @a h_payto account into table.
   16447             :  *
   16448             :  * @param cls closure
   16449             :  * @param provider_section provider that must be checked
   16450             :  * @param h_payto account that must be KYC'ed
   16451             :  * @param[out] requirement_row set to legitimization requirement row for this check
   16452             :  * @return database transaction status
   16453             :  */
   16454             : static enum GNUNET_DB_QueryStatus
   16455           0 : postgres_insert_kyc_requirement_for_account (
   16456             :   void *cls,
   16457             :   const char *provider_section,
   16458             :   const struct TALER_PaytoHashP *h_payto,
   16459             :   uint64_t *requirement_row)
   16460             : {
   16461           0 :   struct PostgresClosure *pg = cls;
   16462           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16463           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
   16464           0 :     GNUNET_PQ_query_param_string (provider_section),
   16465             :     GNUNET_PQ_query_param_end
   16466             :   };
   16467           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   16468           0 :     GNUNET_PQ_result_spec_uint64 ("legitimization_requirement_serial_id",
   16469             :                                   requirement_row),
   16470             :     GNUNET_PQ_result_spec_end
   16471             :   };
   16472             : 
   16473           0 :   return GNUNET_PQ_eval_prepared_singleton_select (
   16474             :     pg->conn,
   16475             :     "insert_legitimization_requirement",
   16476             :     params,
   16477             :     rs);
   16478             : }
   16479             : 
   16480             : 
   16481             : /**
   16482             :  * Begin KYC requirement process.
   16483             :  *
   16484             :  * @param cls closure
   16485             :  * @param h_payto account that must be KYC'ed
   16486             :  * @param provider_section provider that must be checked
   16487             :  * @param provider_account_id provider account ID
   16488             :  * @param provider_legitimization_id provider legitimization ID
   16489             :  * @param[out] process_row row the process is stored under
   16490             :  * @return database transaction status
   16491             :  */
   16492             : static enum GNUNET_DB_QueryStatus
   16493           0 : postgres_insert_kyc_requirement_process (
   16494             :   void *cls,
   16495             :   const struct TALER_PaytoHashP *h_payto,
   16496             :   const char *provider_section,
   16497             :   const char *provider_account_id,
   16498             :   const char *provider_legitimization_id,
   16499             :   uint64_t *process_row)
   16500             : {
   16501           0 :   struct PostgresClosure *pg = cls;
   16502           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16503           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
   16504           0 :     GNUNET_PQ_query_param_string (provider_section),
   16505             :     (NULL != provider_account_id)
   16506           0 :     ? GNUNET_PQ_query_param_string (provider_account_id)
   16507           0 :     : GNUNET_PQ_query_param_null (),
   16508             :     (NULL != provider_legitimization_id)
   16509           0 :     ? GNUNET_PQ_query_param_string (provider_legitimization_id)
   16510           0 :     : GNUNET_PQ_query_param_null (),
   16511             :     GNUNET_PQ_query_param_end
   16512             :   };
   16513           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   16514           0 :     GNUNET_PQ_result_spec_uint64 ("legitimization_process_serial_id",
   16515             :                                   process_row),
   16516             :     GNUNET_PQ_result_spec_end
   16517             :   };
   16518             : 
   16519           0 :   return GNUNET_PQ_eval_prepared_singleton_select (
   16520             :     pg->conn,
   16521             :     "insert_legitimization_process",
   16522             :     params,
   16523             :     rs);
   16524             : }
   16525             : 
   16526             : 
   16527             : /**
   16528             :  * Update KYC requirement check with provider-linkage and/or
   16529             :  * expiration data.
   16530             :  *
   16531             :  * @param cls closure
   16532             :  * @param process_row row to select by
   16533             :  * @param provider_section provider that must be checked (technically redundant)
   16534             :  * @param h_payto account that must be KYC'ed (helps access by shard, otherwise also redundant)
   16535             :  * @param provider_account_id provider account ID
   16536             :  * @param provider_legitimization_id provider legitimization ID
   16537             :  * @param expiration how long is this KYC check set to be valid (in the past if invalid)
   16538             :  * @return database transaction status
   16539             :  */
   16540             : static enum GNUNET_DB_QueryStatus
   16541           0 : postgres_update_kyc_process_by_row (
   16542             :   void *cls,
   16543             :   uint64_t process_row,
   16544             :   const char *provider_section,
   16545             :   const struct TALER_PaytoHashP *h_payto,
   16546             :   const char *provider_account_id,
   16547             :   const char *provider_legitimization_id,
   16548             :   struct GNUNET_TIME_Absolute expiration)
   16549             : {
   16550           0 :   struct PostgresClosure *pg = cls;
   16551           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16552           0 :     GNUNET_PQ_query_param_uint64 (&process_row),
   16553           0 :     GNUNET_PQ_query_param_string (provider_section),
   16554           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
   16555             :     (NULL != provider_account_id)
   16556           0 :     ? GNUNET_PQ_query_param_string (provider_account_id)
   16557           0 :     : GNUNET_PQ_query_param_null (),
   16558             :     (NULL != provider_legitimization_id)
   16559           0 :     ? GNUNET_PQ_query_param_string (provider_legitimization_id)
   16560           0 :     : GNUNET_PQ_query_param_null (),
   16561           0 :     GNUNET_PQ_query_param_absolute_time (&expiration),
   16562             :     GNUNET_PQ_query_param_end
   16563             :   };
   16564             :   enum GNUNET_DB_QueryStatus qs;
   16565             : 
   16566           0 :   qs = GNUNET_PQ_eval_prepared_non_select (
   16567             :     pg->conn,
   16568             :     "update_legitimization_process",
   16569             :     params);
   16570           0 :   if (qs <= 0)
   16571             :   {
   16572           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   16573             :                 "Failed to update legitimization process: %d\n",
   16574             :                 qs);
   16575           0 :     return qs;
   16576             :   }
   16577           0 :   if (GNUNET_TIME_absolute_is_future (expiration))
   16578             :   {
   16579             :     enum GNUNET_DB_QueryStatus qs2;
   16580           0 :     struct TALER_KycCompletedEventP rep = {
   16581           0 :       .header.size = htons (sizeof (rep)),
   16582           0 :       .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
   16583             :       .h_payto = *h_payto
   16584             :     };
   16585           0 :     uint32_t trigger_type = 1;
   16586           0 :     struct GNUNET_PQ_QueryParam params2[] = {
   16587           0 :       GNUNET_PQ_query_param_auto_from_type (h_payto),
   16588           0 :       GNUNET_PQ_query_param_uint32 (&trigger_type),
   16589             :       GNUNET_PQ_query_param_end
   16590             :     };
   16591             : 
   16592           0 :     postgres_event_notify (pg,
   16593             :                            &rep.header,
   16594             :                            NULL,
   16595             :                            0);
   16596           0 :     qs2 = GNUNET_PQ_eval_prepared_non_select (
   16597             :       pg->conn,
   16598             :       "alert_kyc_status_change",
   16599             :       params2);
   16600           0 :     if (qs2 < 0)
   16601           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   16602             :                   "Failed to store KYC alert: %d\n",
   16603             :                   qs2);
   16604             :   }
   16605           0 :   return qs;
   16606             : }
   16607             : 
   16608             : 
   16609             : /**
   16610             :  * Lookup KYC requirement.
   16611             :  *
   16612             :  * @param cls closure
   16613             :  * @param requirement_row identifies requirement to look up
   16614             :  * @param[out] requirements provider that must be checked
   16615             :  * @param[out] h_payto account that must be KYC'ed
   16616             :  * @return database transaction status
   16617             :  */
   16618             : static enum GNUNET_DB_QueryStatus
   16619           0 : postgres_lookup_kyc_requirement_by_row (
   16620             :   void *cls,
   16621             :   uint64_t requirement_row,
   16622             :   char **requirements,
   16623             :   struct TALER_PaytoHashP *h_payto)
   16624             : {
   16625           0 :   struct PostgresClosure *pg = cls;
   16626           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16627           0 :     GNUNET_PQ_query_param_uint64 (&requirement_row),
   16628             :     GNUNET_PQ_query_param_end
   16629             :   };
   16630           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   16631           0 :     GNUNET_PQ_result_spec_string ("required_checks",
   16632             :                                   requirements),
   16633           0 :     GNUNET_PQ_result_spec_auto_from_type ("h_payto",
   16634             :                                           h_payto),
   16635             :     GNUNET_PQ_result_spec_end
   16636             :   };
   16637             : 
   16638           0 :   return GNUNET_PQ_eval_prepared_singleton_select (
   16639             :     pg->conn,
   16640             :     "lookup_legitimization_requirement_by_row",
   16641             :     params,
   16642             :     rs);
   16643             : }
   16644             : 
   16645             : 
   16646             : /**
   16647             :  * Lookup KYC provider meta data.
   16648             :  *
   16649             :  * @param cls closure
   16650             :  * @param provider_section provider that must be checked
   16651             :  * @param h_payto account that must be KYC'ed
   16652             :  * @param[out] process_row row with the legitimization data
   16653             :  * @param[out] expiration how long is this KYC check set to be valid (in the past if invalid)
   16654             :  * @param[out] provider_account_id provider account ID
   16655             :  * @param[out] provider_legitimization_id provider legitimization ID
   16656             :  * @return database transaction status
   16657             :  */
   16658             : static enum GNUNET_DB_QueryStatus
   16659           0 : postgres_lookup_kyc_process_by_account (
   16660             :   void *cls,
   16661             :   const char *provider_section,
   16662             :   const struct TALER_PaytoHashP *h_payto,
   16663             :   uint64_t *process_row,
   16664             :   struct GNUNET_TIME_Absolute *expiration,
   16665             :   char **provider_account_id,
   16666             :   char **provider_legitimization_id)
   16667             : {
   16668           0 :   struct PostgresClosure *pg = cls;
   16669           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16670           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
   16671           0 :     GNUNET_PQ_query_param_string (provider_section),
   16672             :     GNUNET_PQ_query_param_end
   16673             :   };
   16674           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   16675           0 :     GNUNET_PQ_result_spec_uint64 ("legitimization_process_serial_id",
   16676             :                                   process_row),
   16677           0 :     GNUNET_PQ_result_spec_absolute_time ("expiration_time",
   16678             :                                          expiration),
   16679           0 :     GNUNET_PQ_result_spec_allow_null (
   16680             :       GNUNET_PQ_result_spec_string ("provider_user_id",
   16681             :                                     provider_account_id),
   16682             :       NULL),
   16683           0 :     GNUNET_PQ_result_spec_allow_null (
   16684             :       GNUNET_PQ_result_spec_string ("provider_legitimization_id",
   16685             :                                     provider_legitimization_id),
   16686             :       NULL),
   16687             :     GNUNET_PQ_result_spec_end
   16688             :   };
   16689             : 
   16690           0 :   *provider_account_id = NULL;
   16691           0 :   *provider_legitimization_id = NULL;
   16692           0 :   return GNUNET_PQ_eval_prepared_singleton_select (
   16693             :     pg->conn,
   16694             :     "lookup_process_by_account",
   16695             :     params,
   16696             :     rs);
   16697             : }
   16698             : 
   16699             : 
   16700             : /**
   16701             :  * Lookup an
   16702             :  * @a h_payto by @a provider_legitimization_id.
   16703             :  *
   16704             :  * @param cls closure
   16705             :  * @param provider_section
   16706             :  * @param provider_legitimization_id legi to look up
   16707             :  * @param[out] h_payto where to write the result
   16708             :  * @param[out] process_row where to write the row of the entry
   16709             :  * @return database transaction status
   16710             :  */
   16711             : static enum GNUNET_DB_QueryStatus
   16712           0 : postgres_kyc_provider_account_lookup (
   16713             :   void *cls,
   16714             :   const char *provider_section,
   16715             :   const char *provider_legitimization_id,
   16716             :   struct TALER_PaytoHashP *h_payto,
   16717             :   uint64_t *process_row)
   16718             : {
   16719           0 :   struct PostgresClosure *pg = cls;
   16720           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16721           0 :     GNUNET_PQ_query_param_string (provider_section),
   16722           0 :     GNUNET_PQ_query_param_string (provider_legitimization_id),
   16723             :     GNUNET_PQ_query_param_end
   16724             :   };
   16725           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
   16726           0 :     GNUNET_PQ_result_spec_auto_from_type ("h_payto",
   16727             :                                           h_payto),
   16728           0 :     GNUNET_PQ_result_spec_uint64 ("legitimization_process_serial_id",
   16729             :                                   process_row),
   16730             :     GNUNET_PQ_result_spec_end
   16731             :   };
   16732             : 
   16733           0 :   return GNUNET_PQ_eval_prepared_singleton_select (
   16734             :     pg->conn,
   16735             :     "get_wire_target_by_legitimization_id",
   16736             :     params,
   16737             :     rs);
   16738             : }
   16739             : 
   16740             : 
   16741             : /**
   16742             :  * Closure for #get_wire_fees_cb().
   16743             :  */
   16744             : struct GetLegitimizationsContext
   16745             : {
   16746             :   /**
   16747             :    * Function to call per result.
   16748             :    */
   16749             :   TALER_EXCHANGEDB_SatisfiedProviderCallback cb;
   16750             : 
   16751             :   /**
   16752             :    * Closure for @e cb.
   16753             :    */
   16754             :   void *cb_cls;
   16755             : 
   16756             :   /**
   16757             :    * Plugin context.
   16758             :    */
   16759             :   struct PostgresClosure *pg;
   16760             : 
   16761             :   /**
   16762             :    * Flag set to #GNUNET_OK as long as everything is fine.
   16763             :    */
   16764             :   enum GNUNET_GenericReturnValue status;
   16765             : 
   16766             : };
   16767             : 
   16768             : 
   16769             : /**
   16770             :  * Invoke the callback for each result.
   16771             :  *
   16772             :  * @param cls a `struct GetLegitimizationsContext *`
   16773             :  * @param result SQL result
   16774             :  * @param num_results number of rows in @a result
   16775             :  */
   16776             : static void
   16777           0 : get_legitimizations_cb (void *cls,
   16778             :                         PGresult *result,
   16779             :                         unsigned int num_results)
   16780             : {
   16781           0 :   struct GetLegitimizationsContext *ctx = cls;
   16782             : 
   16783           0 :   for (unsigned int i = 0; i < num_results; i++)
   16784             :   {
   16785             :     char *provider_section;
   16786           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   16787           0 :       GNUNET_PQ_result_spec_string ("provider_section",
   16788             :                                     &provider_section),
   16789             :       GNUNET_PQ_result_spec_end
   16790             :     };
   16791             : 
   16792           0 :     if (GNUNET_OK !=
   16793           0 :         GNUNET_PQ_extract_result (result,
   16794             :                                   rs,
   16795             :                                   i))
   16796             :     {
   16797           0 :       GNUNET_break (0);
   16798           0 :       ctx->status = GNUNET_SYSERR;
   16799           0 :       return;
   16800             :     }
   16801           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   16802             :                 "Found satisfied LEGI: %s\n",
   16803             :                 provider_section);
   16804           0 :     ctx->cb (ctx->cb_cls,
   16805             :              provider_section);
   16806           0 :     GNUNET_PQ_cleanup_result (rs);
   16807             :   }
   16808             : }
   16809             : 
   16810             : 
   16811             : /**
   16812             :  * Call us on KYC processes satisfied for the given
   16813             :  * account.
   16814             :  *
   16815             :  * @param cls the @e cls of this struct with the plugin-specific state
   16816             :  * @param h_payto account identifier
   16817             :  * @param spc function to call for each satisfied KYC process
   16818             :  * @param spc_cls closure for @a spc
   16819             :  * @return transaction status code
   16820             :  */
   16821             : static enum GNUNET_DB_QueryStatus
   16822           0 : postgres_select_satisfied_kyc_processes (
   16823             :   void *cls,
   16824             :   const struct TALER_PaytoHashP *h_payto,
   16825             :   TALER_EXCHANGEDB_SatisfiedProviderCallback spc,
   16826             :   void *spc_cls)
   16827             : {
   16828           0 :   struct PostgresClosure *pg = cls;
   16829             :   struct GNUNET_TIME_Absolute now
   16830           0 :     = GNUNET_TIME_absolute_get ();
   16831           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16832           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
   16833           0 :     GNUNET_PQ_query_param_absolute_time (&now),
   16834             :     GNUNET_PQ_query_param_end
   16835             :   };
   16836           0 :   struct GetLegitimizationsContext ctx = {
   16837             :     .cb = spc,
   16838             :     .cb_cls = spc_cls,
   16839             :     .pg = pg,
   16840             :     .status = GNUNET_OK
   16841             :   };
   16842             :   enum GNUNET_DB_QueryStatus qs;
   16843             : 
   16844           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (
   16845             :     pg->conn,
   16846             :     "get_satisfied_legitimizations",
   16847             :     params,
   16848             :     &get_legitimizations_cb,
   16849             :     &ctx);
   16850           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   16851             :               "Satisfied LEGI check returned %d\n",
   16852             :               qs);
   16853           0 :   if (GNUNET_OK != ctx.status)
   16854           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   16855           0 :   return qs;
   16856             : }
   16857             : 
   16858             : 
   16859             : /**
   16860             :  * Closure for #get_kyc_amounts_cb().
   16861             :  */
   16862             : struct KycAmountCheckContext
   16863             : {
   16864             :   /**
   16865             :    * Function to call per result.
   16866             :    */
   16867             :   TALER_EXCHANGEDB_KycAmountCallback cb;
   16868             : 
   16869             :   /**
   16870             :    * Closure for @e cb.
   16871             :    */
   16872             :   void *cb_cls;
   16873             : 
   16874             :   /**
   16875             :    * Plugin context.
   16876             :    */
   16877             :   struct PostgresClosure *pg;
   16878             : 
   16879             :   /**
   16880             :    * Flag set to #GNUNET_OK as long as everything is fine.
   16881             :    */
   16882             :   enum GNUNET_GenericReturnValue status;
   16883             : 
   16884             : };
   16885             : 
   16886             : 
   16887             : /**
   16888             :  * Invoke the callback for each result.
   16889             :  *
   16890             :  * @param cls a `struct KycAmountCheckContext *`
   16891             :  * @param result SQL result
   16892             :  * @param num_results number of rows in @a result
   16893             :  */
   16894             : static void
   16895           0 : get_kyc_amounts_cb (void *cls,
   16896             :                     PGresult *result,
   16897             :                     unsigned int num_results)
   16898             : {
   16899           0 :   struct KycAmountCheckContext *ctx = cls;
   16900           0 :   struct PostgresClosure *pg = ctx->pg;
   16901             : 
   16902           0 :   for (unsigned int i = 0; i < num_results; i++)
   16903             :   {
   16904             :     struct GNUNET_TIME_Absolute date;
   16905             :     struct TALER_Amount amount;
   16906           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
   16907           0 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
   16908             :                                    &amount),
   16909           0 :       GNUNET_PQ_result_spec_absolute_time ("date",
   16910             :                                            &date),
   16911             :       GNUNET_PQ_result_spec_end
   16912             :     };
   16913             :     enum GNUNET_GenericReturnValue ret;
   16914             : 
   16915           0 :     if (GNUNET_OK !=
   16916           0 :         GNUNET_PQ_extract_result (result,
   16917             :                                   rs,
   16918             :                                   i))
   16919             :     {
   16920           0 :       GNUNET_break (0);
   16921           0 :       ctx->status = GNUNET_SYSERR;
   16922           0 :       return;
   16923             :     }
   16924           0 :     ret = ctx->cb (ctx->cb_cls,
   16925             :                    &amount,
   16926             :                    date);
   16927           0 :     GNUNET_PQ_cleanup_result (rs);
   16928           0 :     switch (ret)
   16929             :     {
   16930           0 :     case GNUNET_OK:
   16931           0 :       continue;
   16932           0 :     case GNUNET_NO:
   16933           0 :       break;
   16934           0 :     case GNUNET_SYSERR:
   16935           0 :       ctx->status = GNUNET_SYSERR;
   16936           0 :       break;
   16937             :     }
   16938           0 :     break;
   16939             :   }
   16940             : }
   16941             : 
   16942             : 
   16943             : /**
   16944             :  * Call @a kac on withdrawn amounts after @a time_limit which are relevant
   16945             :  * for a KYC trigger for a the (debited) account identified by @a h_payto.
   16946             :  *
   16947             :  * @param cls the @e cls of this struct with the plugin-specific state
   16948             :  * @param h_payto account identifier
   16949             :  * @param time_limit oldest transaction that could be relevant
   16950             :  * @param kac function to call for each applicable amount, in reverse chronological order (or until @a kac aborts by returning anything except #GNUNET_OK).
   16951             :  * @param kac_cls closure for @a kac
   16952             :  * @return transaction status code, @a kac aborting with #GNUNET_NO is not an error
   16953             :  */
   16954             : static enum GNUNET_DB_QueryStatus
   16955           0 : postgres_select_withdraw_amounts_for_kyc_check (
   16956             :   void *cls,
   16957             :   const struct TALER_PaytoHashP *h_payto,
   16958             :   struct GNUNET_TIME_Absolute time_limit,
   16959             :   TALER_EXCHANGEDB_KycAmountCallback kac,
   16960             :   void *kac_cls)
   16961             : {
   16962           0 :   struct PostgresClosure *pg = cls;
   16963           0 :   struct GNUNET_PQ_QueryParam params[] = {
   16964           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
   16965           0 :     GNUNET_PQ_query_param_absolute_time (&time_limit),
   16966             :     GNUNET_PQ_query_param_end
   16967             :   };
   16968           0 :   struct KycAmountCheckContext ctx = {
   16969             :     .cb = kac,
   16970             :     .cb_cls = kac_cls,
   16971             :     .pg = pg,
   16972             :     .status = GNUNET_OK
   16973             :   };
   16974             :   enum GNUNET_DB_QueryStatus qs;
   16975             : 
   16976           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (
   16977             :     pg->conn,
   16978             :     "select_kyc_relevant_withdraw_events",
   16979             :     params,
   16980             :     &get_kyc_amounts_cb,
   16981             :     &ctx);
   16982           0 :   if (GNUNET_OK != ctx.status)
   16983           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   16984           0 :   return qs;
   16985             : }
   16986             : 
   16987             : 
   16988             : /**
   16989             :  * Call @a kac on deposited amounts after @a time_limit which are relevant for a
   16990             :  * KYC trigger for a the (credited) account identified by @a h_payto.
   16991             :  *
   16992             :  * @param cls the @e cls of this struct with the plugin-specific state
   16993             :  * @param h_payto account identifier
   16994             :  * @param time_limit oldest transaction that could be relevant
   16995             :  * @param kac function to call for each applicable amount, in reverse chronological order (or until @a kac aborts by returning anything except #GNUNET_OK).
   16996             :  * @param kac_cls closure for @a kac
   16997             :  * @return transaction status code, @a kac aborting with #GNUNET_NO is not an error
   16998             :  */
   16999             : static enum GNUNET_DB_QueryStatus
   17000           0 : postgres_select_aggregation_amounts_for_kyc_check (
   17001             :   void *cls,
   17002             :   const struct TALER_PaytoHashP *h_payto,
   17003             :   struct GNUNET_TIME_Absolute time_limit,
   17004             :   TALER_EXCHANGEDB_KycAmountCallback kac,
   17005             :   void *kac_cls)
   17006             : {
   17007           0 :   struct PostgresClosure *pg = cls;
   17008           0 :   struct GNUNET_PQ_QueryParam params[] = {
   17009           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
   17010           0 :     GNUNET_PQ_query_param_absolute_time (&time_limit),
   17011             :     GNUNET_PQ_query_param_end
   17012             :   };
   17013           0 :   struct KycAmountCheckContext ctx = {
   17014             :     .cb = kac,
   17015             :     .cb_cls = kac_cls,
   17016             :     .pg = pg,
   17017             :     .status = GNUNET_OK
   17018             :   };
   17019             :   enum GNUNET_DB_QueryStatus qs;
   17020             : 
   17021           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (
   17022             :     pg->conn,
   17023             :     "select_kyc_relevant_aggregation_events",
   17024             :     params,
   17025             :     &get_kyc_amounts_cb,
   17026             :     &ctx);
   17027           0 :   if (GNUNET_OK != ctx.status)
   17028           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   17029           0 :   return qs;
   17030             : }
   17031             : 
   17032             : 
   17033             : /**
   17034             :  * Call @a kac on merged reserve amounts after @a time_limit which are relevant for a
   17035             :  * KYC trigger for a the wallet identified by @a h_payto.
   17036             :  *
   17037             :  * @param cls the @e cls of this struct with the plugin-specific state
   17038             :  * @param h_payto account identifier
   17039             :  * @param time_limit oldest transaction that could be relevant
   17040             :  * @param kac function to call for each applicable amount, in reverse chronological order (or until @a kac aborts by returning anything except #GNUNET_OK).
   17041             :  * @param kac_cls closure for @a kac
   17042             :  * @return transaction status code, @a kac aborting with #GNUNET_NO is not an error
   17043             :  */
   17044             : static enum GNUNET_DB_QueryStatus
   17045           0 : postgres_select_merge_amounts_for_kyc_check (
   17046             :   void *cls,
   17047             :   const struct TALER_PaytoHashP *h_payto,
   17048             :   struct GNUNET_TIME_Absolute time_limit,
   17049             :   TALER_EXCHANGEDB_KycAmountCallback kac,
   17050             :   void *kac_cls)
   17051             : {
   17052           0 :   struct PostgresClosure *pg = cls;
   17053           0 :   struct GNUNET_PQ_QueryParam params[] = {
   17054           0 :     GNUNET_PQ_query_param_auto_from_type (h_payto),
   17055           0 :     GNUNET_PQ_query_param_absolute_time (&time_limit),
   17056             :     GNUNET_PQ_query_param_end
   17057             :   };
   17058           0 :   struct KycAmountCheckContext ctx = {
   17059             :     .cb = kac,
   17060             :     .cb_cls = kac_cls,
   17061             :     .pg = pg,
   17062             :     .status = GNUNET_OK
   17063             :   };
   17064             :   enum GNUNET_DB_QueryStatus qs;
   17065             : 
   17066           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (
   17067             :     pg->conn,
   17068             :     "select_kyc_relevant_merge_events",
   17069             :     params,
   17070             :     &get_kyc_amounts_cb,
   17071             :     &ctx);
   17072           0 :   if (GNUNET_OK != ctx.status)
   17073           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
   17074           0 :   return qs;
   17075             : }
   17076             : 
   17077             : 
   17078             : /**
   17079             :  * Initialize Postgres database subsystem.
   17080             :  *
   17081             :  * @param cls a configuration instance
   17082             :  * @return NULL on error, otherwise a `struct
   17083             :  *         TALER_EXCHANGEDB_Plugin`
   17084             :  */
   17085             : void *
   17086          21 : libtaler_plugin_exchangedb_postgres_init (void *cls)
   17087             : {
   17088          21 :   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
   17089             :   struct PostgresClosure *pg;
   17090             :   struct TALER_EXCHANGEDB_Plugin *plugin;
   17091             : 
   17092          21 :   pg = GNUNET_new (struct PostgresClosure);
   17093          21 :   pg->cfg = cfg;
   17094          21 :   if (GNUNET_OK !=
   17095          21 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
   17096             :                                                "exchangedb-postgres",
   17097             :                                                "SQL_DIR",
   17098             :                                                &pg->sql_dir))
   17099             :   {
   17100           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   17101             :                                "exchangedb-postgres",
   17102             :                                "CONFIG");
   17103           0 :     GNUNET_free (pg);
   17104           0 :     return NULL;
   17105             :   }
   17106          21 :   if (GNUNET_OK !=
   17107          21 :       GNUNET_CONFIGURATION_get_value_string (cfg,
   17108             :                                              "exchange",
   17109             :                                              "BASE_URL",
   17110             :                                              &pg->exchange_url))
   17111             :   {
   17112           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   17113             :                                "exchange",
   17114             :                                "BASE_URL");
   17115           0 :     GNUNET_free (pg->sql_dir);
   17116           0 :     GNUNET_free (pg);
   17117           0 :     return NULL;
   17118             :   }
   17119          21 :   if ( (GNUNET_OK !=
   17120          21 :         GNUNET_CONFIGURATION_get_value_time (cfg,
   17121             :                                              "exchangedb",
   17122             :                                              "IDLE_RESERVE_EXPIRATION_TIME",
   17123             :                                              &pg->idle_reserve_expiration_time))
   17124          21 :        ||
   17125             :        (GNUNET_OK !=
   17126          21 :         GNUNET_CONFIGURATION_get_value_time (cfg,
   17127             :                                              "exchangedb",
   17128             :                                              "LEGAL_RESERVE_EXPIRATION_TIME",
   17129             :                                              &pg->legal_reserve_expiration_time)) )
   17130             :   {
   17131           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   17132             :                                "exchangedb",
   17133             :                                "LEGAL/IDLE_RESERVE_EXPIRATION_TIME");
   17134           0 :     GNUNET_free (pg->exchange_url);
   17135           0 :     GNUNET_free (pg->sql_dir);
   17136           0 :     GNUNET_free (pg);
   17137           0 :     return NULL;
   17138             :   }
   17139          21 :   if (GNUNET_OK !=
   17140          21 :       GNUNET_CONFIGURATION_get_value_time (cfg,
   17141             :                                            "exchangedb",
   17142             :                                            "AGGREGATOR_SHIFT",
   17143             :                                            &pg->aggregator_shift))
   17144             :   {
   17145           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
   17146             :                                "exchangedb",
   17147             :                                "AGGREGATOR_SHIFT");
   17148             :   }
   17149             : 
   17150          21 :   if (GNUNET_OK !=
   17151          21 :       TALER_config_get_currency (cfg,
   17152             :                                  &pg->currency))
   17153             :   {
   17154           0 :     GNUNET_free (pg->exchange_url);
   17155           0 :     GNUNET_free (pg->sql_dir);
   17156           0 :     GNUNET_free (pg);
   17157           0 :     return NULL;
   17158             :   }
   17159          21 :   if (GNUNET_OK !=
   17160          21 :       internal_setup (pg,
   17161             :                       true))
   17162             :   {
   17163          21 :     GNUNET_free (pg->exchange_url);
   17164          21 :     GNUNET_free (pg->currency);
   17165          21 :     GNUNET_free (pg->sql_dir);
   17166          21 :     GNUNET_free (pg);
   17167          21 :     return NULL;
   17168             :   }
   17169             : 
   17170           0 :   plugin = GNUNET_new (struct TALER_EXCHANGEDB_Plugin);
   17171           0 :   plugin->cls = pg;
   17172           0 :   plugin->drop_tables = &postgres_drop_tables;
   17173           0 :   plugin->create_tables = &postgres_create_tables;
   17174           0 :   plugin->create_shard_tables = &postgres_create_shard_tables;
   17175           0 :   plugin->setup_partitions = &postgres_setup_partitions;
   17176           0 :   plugin->setup_foreign_servers = &postgres_setup_foreign_servers;
   17177           0 :   plugin->start = &postgres_start;
   17178           0 :   plugin->start_read_committed = &postgres_start_read_committed;
   17179           0 :   plugin->start_read_only = &postgres_start_read_only;
   17180           0 :   plugin->commit = &postgres_commit;
   17181           0 :   plugin->preflight = &postgres_preflight;
   17182           0 :   plugin->rollback = &postgres_rollback;
   17183           0 :   plugin->event_listen = &postgres_event_listen;
   17184           0 :   plugin->event_listen_cancel = &postgres_event_listen_cancel;
   17185           0 :   plugin->event_notify = &postgres_event_notify;
   17186           0 :   plugin->insert_denomination_info = &postgres_insert_denomination_info;
   17187           0 :   plugin->get_denomination_info = &postgres_get_denomination_info;
   17188           0 :   plugin->iterate_denomination_info = &postgres_iterate_denomination_info;
   17189           0 :   plugin->iterate_denominations = &postgres_iterate_denominations;
   17190           0 :   plugin->iterate_active_signkeys = &postgres_iterate_active_signkeys;
   17191           0 :   plugin->iterate_active_auditors = &postgres_iterate_active_auditors;
   17192           0 :   plugin->iterate_auditor_denominations =
   17193             :     &postgres_iterate_auditor_denominations;
   17194           0 :   plugin->reserves_get = &postgres_reserves_get;
   17195           0 :   plugin->reserves_get_origin = &postgres_reserves_get_origin;
   17196           0 :   plugin->drain_kyc_alert = &postgres_drain_kyc_alert;
   17197           0 :   plugin->reserves_in_insert = &postgres_reserves_in_insert;
   17198           0 :   plugin->get_withdraw_info = &postgres_get_withdraw_info;
   17199           0 :   plugin->do_withdraw = &postgres_do_withdraw;
   17200           0 :   plugin->do_batch_withdraw = &postgres_do_batch_withdraw;
   17201           0 :   plugin->do_batch_withdraw_insert = &postgres_do_batch_withdraw_insert;
   17202           0 :   plugin->do_deposit = &postgres_do_deposit;
   17203           0 :   plugin->do_melt = &postgres_do_melt;
   17204           0 :   plugin->do_refund = &postgres_do_refund;
   17205           0 :   plugin->do_recoup = &postgres_do_recoup;
   17206           0 :   plugin->do_recoup_refresh = &postgres_do_recoup_refresh;
   17207           0 :   plugin->get_reserve_balance = &postgres_get_reserve_balance;
   17208           0 :   plugin->get_reserve_history = &postgres_get_reserve_history;
   17209           0 :   plugin->get_reserve_status = &postgres_get_reserve_status;
   17210           0 :   plugin->free_reserve_history = &common_free_reserve_history;
   17211           0 :   plugin->count_known_coins = &postgres_count_known_coins;
   17212           0 :   plugin->ensure_coin_known = &postgres_ensure_coin_known;
   17213           0 :   plugin->get_known_coin = &postgres_get_known_coin;
   17214           0 :   plugin->get_coin_denomination = &postgres_get_coin_denomination;
   17215           0 :   plugin->have_deposit2 = &postgres_have_deposit2;
   17216           0 :   plugin->aggregate = &postgres_aggregate;
   17217             :   plugin->create_aggregation_transient
   17218           0 :     = &postgres_create_aggregation_transient;
   17219             :   plugin->select_aggregation_transient
   17220           0 :     = &postgres_select_aggregation_transient;
   17221             :   plugin->find_aggregation_transient
   17222           0 :     = &postgres_find_aggregation_transient;
   17223             :   plugin->update_aggregation_transient
   17224           0 :     = &postgres_update_aggregation_transient;
   17225             :   plugin->delete_aggregation_transient
   17226           0 :     = &postgres_delete_aggregation_transient;
   17227           0 :   plugin->get_ready_deposit = &postgres_get_ready_deposit;
   17228           0 :   plugin->insert_deposit = &postgres_insert_deposit;
   17229           0 :   plugin->insert_refund = &postgres_insert_refund;
   17230           0 :   plugin->select_refunds_by_coin = &postgres_select_refunds_by_coin;
   17231           0 :   plugin->get_melt = &postgres_get_melt;
   17232           0 :   plugin->insert_refresh_reveal = &postgres_insert_refresh_reveal;
   17233           0 :   plugin->get_refresh_reveal = &postgres_get_refresh_reveal;
   17234           0 :   plugin->get_link_data = &postgres_get_link_data;
   17235           0 :   plugin->get_coin_transactions = &postgres_get_coin_transactions;
   17236           0 :   plugin->free_coin_transaction_list = &common_free_coin_transaction_list;
   17237           0 :   plugin->lookup_wire_transfer = &postgres_lookup_wire_transfer;
   17238           0 :   plugin->lookup_transfer_by_deposit = &postgres_lookup_transfer_by_deposit;
   17239           0 :   plugin->insert_aggregation_tracking = &postgres_insert_aggregation_tracking;
   17240           0 :   plugin->insert_wire_fee = &postgres_insert_wire_fee;
   17241           0 :   plugin->insert_global_fee = &postgres_insert_global_fee;
   17242           0 :   plugin->get_wire_fee = &postgres_get_wire_fee;
   17243           0 :   plugin->get_global_fee = &postgres_get_global_fee;
   17244           0 :   plugin->get_global_fees = &postgres_get_global_fees;
   17245           0 :   plugin->get_expired_reserves = &postgres_get_expired_reserves;
   17246           0 :   plugin->insert_reserve_closed = &postgres_insert_reserve_closed;
   17247           0 :   plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert;
   17248           0 :   plugin->wire_prepare_data_mark_finished =
   17249             :     &postgres_wire_prepare_data_mark_finished;
   17250           0 :   plugin->wire_prepare_data_mark_failed =
   17251             :     &postgres_wire_prepare_data_mark_failed;
   17252           0 :   plugin->wire_prepare_data_get = &postgres_wire_prepare_data_get;
   17253           0 :   plugin->start_deferred_wire_out = &postgres_start_deferred_wire_out;
   17254           0 :   plugin->store_wire_transfer_out = &postgres_store_wire_transfer_out;
   17255           0 :   plugin->gc = &postgres_gc;
   17256             :   plugin->select_deposits_above_serial_id
   17257           0 :     = &postgres_select_deposits_above_serial_id;
   17258             :   plugin->select_purse_deposits_above_serial_id
   17259           0 :     = &postgres_select_purse_deposits_above_serial_id;
   17260             :   plugin->select_account_merges_above_serial_id
   17261           0 :     = &postgres_select_account_merges_above_serial_id;
   17262             :   plugin->select_purse_merges_above_serial_id
   17263           0 :     = &postgres_select_purse_merges_above_serial_id;
   17264             :   plugin->select_history_requests_above_serial_id
   17265           0 :     = &postgres_select_history_requests_above_serial_id;
   17266             :   plugin->select_purse_refunds_above_serial_id
   17267           0 :     = &postgres_select_purse_refunds_above_serial_id;
   17268             :   plugin->select_purse_deposits_by_purse
   17269           0 :     = &postgres_select_purse_deposits_by_purse;
   17270             :   plugin->select_refreshes_above_serial_id
   17271           0 :     = &postgres_select_refreshes_above_serial_id;
   17272             :   plugin->select_refunds_above_serial_id
   17273           0 :     = &postgres_select_refunds_above_serial_id;
   17274             :   plugin->select_reserves_in_above_serial_id
   17275           0 :     = &postgres_select_reserves_in_above_serial_id;
   17276             :   plugin->select_reserves_in_above_serial_id_by_account
   17277           0 :     = &postgres_select_reserves_in_above_serial_id_by_account;
   17278             :   plugin->select_withdrawals_above_serial_id
   17279           0 :     = &postgres_select_withdrawals_above_serial_id;
   17280             :   plugin->select_wire_out_above_serial_id
   17281           0 :     = &postgres_select_wire_out_above_serial_id;
   17282             :   plugin->select_wire_out_above_serial_id_by_account
   17283           0 :     = &postgres_select_wire_out_above_serial_id_by_account;
   17284             :   plugin->select_recoup_above_serial_id
   17285           0 :     = &postgres_select_recoup_above_serial_id;
   17286             :   plugin->select_recoup_refresh_above_serial_id
   17287           0 :     = &postgres_select_recoup_refresh_above_serial_id;
   17288             :   plugin->select_reserve_closed_above_serial_id
   17289           0 :     = &postgres_select_reserve_closed_above_serial_id;
   17290             :   plugin->get_reserve_by_h_blind
   17291           0 :     = &postgres_get_reserve_by_h_blind;
   17292             :   plugin->get_old_coin_by_h_blind
   17293           0 :     = &postgres_get_old_coin_by_h_blind;
   17294             :   plugin->insert_denomination_revocation
   17295           0 :     = &postgres_insert_denomination_revocation;
   17296             :   plugin->get_denomination_revocation
   17297           0 :     = &postgres_get_denomination_revocation;
   17298             :   plugin->select_deposits_missing_wire
   17299           0 :     = &postgres_select_deposits_missing_wire;
   17300             :   plugin->lookup_auditor_timestamp
   17301           0 :     = &postgres_lookup_auditor_timestamp;
   17302             :   plugin->lookup_auditor_status
   17303           0 :     = &postgres_lookup_auditor_status;
   17304             :   plugin->insert_auditor
   17305           0 :     = &postgres_insert_auditor;
   17306             :   plugin->update_auditor
   17307           0 :     = &postgres_update_auditor;
   17308             :   plugin->lookup_wire_timestamp
   17309           0 :     = &postgres_lookup_wire_timestamp;
   17310             :   plugin->insert_wire
   17311           0 :     = &postgres_insert_wire;
   17312             :   plugin->update_wire
   17313           0 :     = &postgres_update_wire;
   17314             :   plugin->get_wire_accounts
   17315           0 :     = &postgres_get_wire_accounts;
   17316             :   plugin->get_wire_fees
   17317           0 :     = &postgres_get_wire_fees;
   17318             :   plugin->insert_signkey_revocation
   17319           0 :     = &postgres_insert_signkey_revocation;
   17320             :   plugin->lookup_signkey_revocation
   17321           0 :     = &postgres_lookup_signkey_revocation;
   17322             :   plugin->lookup_denomination_key
   17323           0 :     = &postgres_lookup_denomination_key;
   17324             :   plugin->insert_auditor_denom_sig
   17325           0 :     = &postgres_insert_auditor_denom_sig;
   17326             :   plugin->select_auditor_denom_sig
   17327           0 :     = &postgres_select_auditor_denom_sig;
   17328             :   plugin->lookup_wire_fee_by_time
   17329           0 :     = &postgres_lookup_wire_fee_by_time;
   17330             :   plugin->lookup_global_fee_by_time
   17331           0 :     = &postgres_lookup_global_fee_by_time;
   17332             :   plugin->add_denomination_key
   17333           0 :     = &postgres_add_denomination_key;
   17334             :   plugin->activate_signing_key
   17335           0 :     = &postgres_activate_signing_key;
   17336             :   plugin->lookup_signing_key
   17337           0 :     = &postgres_lookup_signing_key;
   17338             :   plugin->lookup_serial_by_table
   17339           0 :     = &postgres_lookup_serial_by_table;
   17340             :   plugin->lookup_records_by_table
   17341           0 :     = &postgres_lookup_records_by_table;
   17342             :   plugin->insert_records_by_table
   17343           0 :     = &postgres_insert_records_by_table;
   17344             :   plugin->begin_shard
   17345           0 :     = &postgres_begin_shard;
   17346             :   plugin->abort_shard
   17347           0 :     = &postgres_abort_shard;
   17348             :   plugin->complete_shard
   17349           0 :     = &postgres_complete_shard;
   17350             :   plugin->begin_revolving_shard
   17351           0 :     = &postgres_begin_revolving_shard;
   17352             :   plugin->release_revolving_shard
   17353           0 :     = &postgres_release_revolving_shard;
   17354             :   plugin->delete_shard_locks
   17355           0 :     = &postgres_delete_shard_locks;
   17356             :   plugin->set_extension_config
   17357           0 :     = &postgres_set_extension_config;
   17358             :   plugin->get_extension_config
   17359           0 :     = &postgres_get_extension_config;
   17360             :   plugin->insert_partner
   17361           0 :     = &postgres_insert_partner;
   17362             :   plugin->insert_contract
   17363           0 :     = &postgres_insert_contract;
   17364             :   plugin->select_contract
   17365           0 :     = &postgres_select_contract;
   17366             :   plugin->select_contract_by_purse
   17367           0 :     = &postgres_select_contract_by_purse;
   17368             :   plugin->insert_purse_request
   17369           0 :     = &postgres_insert_purse_request;
   17370             :   plugin->select_purse_request
   17371           0 :     = &postgres_select_purse_request;
   17372             :   plugin->expire_purse
   17373           0 :     = &postgres_expire_purse;
   17374             :   plugin->select_purse
   17375           0 :     = &postgres_select_purse;
   17376             :   plugin->select_purse_by_merge_pub
   17377           0 :     = &postgres_select_purse_by_merge_pub;
   17378             :   plugin->do_purse_deposit
   17379           0 :     = &postgres_do_purse_deposit;
   17380             :   plugin->set_purse_balance
   17381           0 :     = &postgres_set_purse_balance;
   17382             :   plugin->get_purse_deposit
   17383           0 :     = &postgres_get_purse_deposit;
   17384             :   plugin->do_purse_merge
   17385           0 :     = &postgres_do_purse_merge;
   17386             :   plugin->do_reserve_purse
   17387           0 :     = &postgres_do_reserve_purse;
   17388             :   plugin->select_purse_merge
   17389           0 :     = &postgres_select_purse_merge;
   17390             :   plugin->insert_history_request
   17391           0 :     = &postgres_insert_history_request;
   17392             :   plugin->insert_close_request
   17393           0 :     = &postgres_insert_close_request;
   17394             :   plugin->insert_drain_profit
   17395           0 :     = &postgres_insert_drain_profit;
   17396             :   plugin->profit_drains_get_pending
   17397           0 :     = &postgres_profit_drains_get_pending;
   17398             :   plugin->get_drain_profit
   17399           0 :     = &postgres_get_drain_profit;
   17400             :   plugin->profit_drains_set_finished
   17401           0 :     = &postgres_profit_drains_set_finished;
   17402             :   plugin->insert_kyc_requirement_for_account
   17403           0 :     = &postgres_insert_kyc_requirement_for_account;
   17404             :   plugin->insert_kyc_requirement_process
   17405           0 :     = &postgres_insert_kyc_requirement_process;
   17406             :   plugin->update_kyc_process_by_row
   17407           0 :     = &postgres_update_kyc_process_by_row;
   17408             :   plugin->lookup_kyc_requirement_by_row
   17409           0 :     = &postgres_lookup_kyc_requirement_by_row;
   17410             :   plugin->lookup_kyc_process_by_account
   17411           0 :     = &postgres_lookup_kyc_process_by_account;
   17412             :   plugin->kyc_provider_account_lookup
   17413           0 :     = &postgres_kyc_provider_account_lookup;
   17414             :   plugin->select_satisfied_kyc_processes
   17415           0 :     = &postgres_select_satisfied_kyc_processes;
   17416             :   plugin->select_withdraw_amounts_for_kyc_check
   17417           0 :     = &postgres_select_withdraw_amounts_for_kyc_check;
   17418             :   plugin->select_aggregation_amounts_for_kyc_check
   17419           0 :     = &postgres_select_aggregation_amounts_for_kyc_check;
   17420             :   plugin->select_merge_amounts_for_kyc_check
   17421           0 :     = &postgres_select_merge_amounts_for_kyc_check;
   17422           0 :   return plugin;
   17423             : }
   17424             : 
   17425             : 
   17426             : /**
   17427             :  * Shutdown Postgres database subsystem.
   17428             :  *
   17429             :  * @param cls a `struct TALER_EXCHANGEDB_Plugin`
   17430             :  * @return NULL (always)
   17431             :  */
   17432             : void *
   17433           0 : libtaler_plugin_exchangedb_postgres_done (void *cls)
   17434             : {
   17435           0 :   struct TALER_EXCHANGEDB_Plugin *plugin = cls;
   17436           0 :   struct PostgresClosure *pg = plugin->cls;
   17437             : 
   17438           0 :   if (NULL != pg->conn)
   17439             :   {
   17440           0 :     GNUNET_PQ_disconnect (pg->conn);
   17441           0 :     pg->conn = NULL;
   17442             :   }
   17443           0 :   GNUNET_free (pg->exchange_url);
   17444           0 :   GNUNET_free (pg->sql_dir);
   17445           0 :   GNUNET_free (pg->currency);
   17446           0 :   GNUNET_free (pg);
   17447           0 :   GNUNET_free (plugin);
   17448           0 :   return NULL;
   17449             : }
   17450             : 
   17451             : 
   17452             : /* end of plugin_exchangedb_postgres.c */

Generated by: LCOV version 1.14