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: 2284 2728 83.7 %
Date: 2021-08-30 06:43:37 Functions: 137 141 97.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014--2021 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_json_lib.h"
      30             : #include "taler_exchangedb_plugin.h"
      31             : #include <poll.h>
      32             : #include <pthread.h>
      33             : #include <sys/eventfd.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             :  * Should we explicitly lock certain individual tables prior to SELECT+INSERT
      47             :  * combis?
      48             :  */
      49             : #define EXPLICIT_LOCKS 0
      50             : 
      51             : /**
      52             :  * Wrapper macro to add the currency from the plugin's state
      53             :  * when fetching amounts from the database.
      54             :  *
      55             :  * @param field name of the database field to fetch amount from
      56             :  * @param[out] amountp pointer to amount to set
      57             :  */
      58             : #define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) TALER_PQ_result_spec_amount ( \
      59             :     field,pg->currency,amountp)
      60             : 
      61             : /**
      62             :  * Wrapper macro to add the currency from the plugin's state
      63             :  * when fetching amounts from the database.  NBO variant.
      64             :  *
      65             :  * @param field name of the database field to fetch amount from
      66             :  * @param[out] amountp pointer to amount to set
      67             :  */
      68             : #define TALER_PQ_RESULT_SPEC_AMOUNT_NBO(field,                          \
      69             :                                         amountp) TALER_PQ_result_spec_amount_nbo ( \
      70             :     field,pg->currency,amountp)
      71             : 
      72             : /**
      73             :  * Log a really unexpected PQ error with all the details we can get hold of.
      74             :  *
      75             :  * @param result PQ result object of the PQ operation that failed
      76             :  * @param conn SQL connection that was used
      77             :  */
      78             : #define BREAK_DB_ERR(result,conn) do {                                  \
      79             :     GNUNET_break (0);                                                   \
      80             :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,                                \
      81             :                 "Database failure: %s/%s/%s/%s/%s",                     \
      82             :                 PQresultErrorField (result, PG_DIAG_MESSAGE_PRIMARY),   \
      83             :                 PQresultErrorField (result, PG_DIAG_MESSAGE_DETAIL),    \
      84             :                 PQresultErrorMessage (result),                          \
      85             :                 PQresStatus (PQresultStatus (result)),                  \
      86             :                 PQerrorMessage (conn));                                 \
      87             : } while (0)
      88             : 
      89             : 
      90             : /**
      91             :  * Type of the "cls" argument given to each of the functions in
      92             :  * our API.
      93             :  */
      94             : struct PostgresClosure
      95             : {
      96             : 
      97             :   /**
      98             :    * Our configuration.
      99             :    */
     100             :   const struct GNUNET_CONFIGURATION_Handle *cfg;
     101             : 
     102             :   /**
     103             :    * Directory with SQL statements to run to create tables.
     104             :    */
     105             :   char *sql_dir;
     106             : 
     107             :   /**
     108             :    * After how long should idle reserves be closed?
     109             :    */
     110             :   struct GNUNET_TIME_Relative idle_reserve_expiration_time;
     111             : 
     112             :   /**
     113             :    * After how long should reserves that have seen withdraw operations
     114             :    * be garbage collected?
     115             :    */
     116             :   struct GNUNET_TIME_Relative legal_reserve_expiration_time;
     117             : 
     118             :   /**
     119             :    * Which currency should we assume all amounts to be in?
     120             :    */
     121             :   char *currency;
     122             : 
     123             :   /**
     124             :    * Postgres connection handle.
     125             :    */
     126             :   struct GNUNET_PQ_Context *conn;
     127             : 
     128             :   /**
     129             :    * Name of the current transaction, for debugging.
     130             :    */
     131             :   const char *transaction_name;
     132             : 
     133             :   /**
     134             :    * Number of registered listerners. @e event_thread
     135             :    * should terminate if this value reaches 0.
     136             :    */
     137             :   uint64_t listener_count;
     138             : 
     139             :   /**
     140             :    * Did we initialize the prepared statements
     141             :    * for this session?
     142             :    */
     143             :   bool init;
     144             : 
     145             : };
     146             : 
     147             : 
     148             : /**
     149             :  * Drop all Taler tables.  This should only be used by testcases.
     150             :  *
     151             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     152             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
     153             :  */
     154             : static int
     155          12 : postgres_drop_tables (void *cls)
     156             : {
     157          12 :   struct PostgresClosure *pg = cls;
     158             :   struct GNUNET_PQ_Context *conn;
     159             : 
     160          12 :   conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
     161             :                                      "exchangedb-postgres",
     162             :                                      "drop",
     163             :                                      NULL,
     164             :                                      NULL);
     165          12 :   if (NULL == conn)
     166           2 :     return GNUNET_SYSERR;
     167          10 :   GNUNET_PQ_disconnect (conn);
     168          10 :   return GNUNET_OK;
     169             : }
     170             : 
     171             : 
     172             : /**
     173             :  * Create the necessary tables if they are not present
     174             :  *
     175             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     176             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
     177             :  */
     178             : static int
     179          48 : postgres_create_tables (void *cls)
     180             : {
     181          48 :   struct PostgresClosure *pg = cls;
     182             :   struct GNUNET_PQ_Context *conn;
     183             : 
     184          48 :   conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
     185             :                                      "exchangedb-postgres",
     186             :                                      "exchange-",
     187             :                                      NULL,
     188             :                                      NULL);
     189          48 :   if (NULL == conn)
     190           0 :     return GNUNET_SYSERR;
     191          48 :   GNUNET_PQ_disconnect (conn);
     192          48 :   return GNUNET_OK;
     193             : }
     194             : 
     195             : 
     196             : /**
     197             :  * Initialize prepared statements for @a pg.
     198             :  *
     199             :  * @param[in,out] pg connection to initialize
     200             :  * @return #GNUNET_OK on success
     201             :  */
     202             : static enum GNUNET_GenericReturnValue
     203         580 : prepare_statements (struct PostgresClosure *pg)
     204             : {
     205             :   enum GNUNET_GenericReturnValue ret;
     206         580 :   struct GNUNET_PQ_PreparedStatement ps[] = {
     207             :     /* Used in #postgres_insert_denomination_info() and
     208             :        #postgres_add_denomination_key() */
     209         580 :     GNUNET_PQ_make_prepare ("denomination_insert",
     210             :                             "INSERT INTO denominations "
     211             :                             "(denom_pub_hash"
     212             :                             ",denom_pub"
     213             :                             ",master_sig"
     214             :                             ",valid_from"
     215             :                             ",expire_withdraw"
     216             :                             ",expire_deposit"
     217             :                             ",expire_legal"
     218             :                             ",coin_val"                                            /* value of this denom */
     219             :                             ",coin_frac"                                            /* fractional value of this denom */
     220             :                             ",fee_withdraw_val"
     221             :                             ",fee_withdraw_frac"
     222             :                             ",fee_deposit_val"
     223             :                             ",fee_deposit_frac"
     224             :                             ",fee_refresh_val"
     225             :                             ",fee_refresh_frac"
     226             :                             ",fee_refund_val"
     227             :                             ",fee_refund_frac"
     228             :                             ") VALUES "
     229             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
     230             :                             " $11, $12, $13, $14, $15, $16, $17);",
     231             :                             17),
     232             :     /* Used in #postgres_iterate_denomination_info() */
     233         580 :     GNUNET_PQ_make_prepare ("denomination_iterate",
     234             :                             "SELECT"
     235             :                             " master_sig"
     236             :                             ",valid_from"
     237             :                             ",expire_withdraw"
     238             :                             ",expire_deposit"
     239             :                             ",expire_legal"
     240             :                             ",coin_val"                                            /* value of this denom */
     241             :                             ",coin_frac"                                            /* fractional value of this denom */
     242             :                             ",fee_withdraw_val"
     243             :                             ",fee_withdraw_frac"
     244             :                             ",fee_deposit_val"
     245             :                             ",fee_deposit_frac"
     246             :                             ",fee_refresh_val"
     247             :                             ",fee_refresh_frac"
     248             :                             ",fee_refund_val"
     249             :                             ",fee_refund_frac"
     250             :                             ",denom_pub"
     251             :                             " FROM denominations;",
     252             :                             0),
     253             :     /* Used in #postgres_iterate_denominations() */
     254         580 :     GNUNET_PQ_make_prepare ("select_denominations",
     255             :                             "SELECT"
     256             :                             " denominations.master_sig"
     257             :                             ",denom_revocations_serial_id IS NOT NULL AS revoked"
     258             :                             ",valid_from"
     259             :                             ",expire_withdraw"
     260             :                             ",expire_deposit"
     261             :                             ",expire_legal"
     262             :                             ",coin_val"                                            /* value of this denom */
     263             :                             ",coin_frac"                                            /* fractional value of this denom */
     264             :                             ",fee_withdraw_val"
     265             :                             ",fee_withdraw_frac"
     266             :                             ",fee_deposit_val"
     267             :                             ",fee_deposit_frac"
     268             :                             ",fee_refresh_val"
     269             :                             ",fee_refresh_frac"
     270             :                             ",fee_refund_val"
     271             :                             ",fee_refund_frac"
     272             :                             ",denom_pub"
     273             :                             " FROM denominations"
     274             :                             " LEFT JOIN "
     275             :                             "   denomination_revocations USING (denominations_serial);",
     276             :                             0),
     277             :     /* Used in #postgres_iterate_active_signkeys() */
     278         580 :     GNUNET_PQ_make_prepare ("select_signkeys",
     279             :                             "SELECT"
     280             :                             " master_sig"
     281             :                             ",exchange_pub"
     282             :                             ",valid_from"
     283             :                             ",expire_sign"
     284             :                             ",expire_legal"
     285             :                             " FROM exchange_sign_keys esk"
     286             :                             " WHERE"
     287             :                             "   expire_sign > $1"
     288             :                             " AND NOT EXISTS "
     289             :                             "  (SELECT esk_serial "
     290             :                             "     FROM signkey_revocations skr"
     291             :                             "    WHERE esk.esk_serial = skr.esk_serial);",
     292             :                             1),
     293             :     /* Used in #postgres_iterate_auditor_denominations() */
     294         580 :     GNUNET_PQ_make_prepare ("select_auditor_denoms",
     295             :                             "SELECT"
     296             :                             " auditors.auditor_pub"
     297             :                             ",denominations.denom_pub_hash"
     298             :                             ",auditor_denom_sigs.auditor_sig"
     299             :                             " FROM auditor_denom_sigs"
     300             :                             " JOIN auditors USING (auditor_uuid)"
     301             :                             " JOIN denominations USING (denominations_serial)"
     302             :                             " WHERE auditors.is_active;",
     303             :                             0),
     304             :     /* Used in #postgres_iterate_active_auditors() */
     305         580 :     GNUNET_PQ_make_prepare ("select_auditors",
     306             :                             "SELECT"
     307             :                             " auditor_pub"
     308             :                             ",auditor_url"
     309             :                             ",auditor_name"
     310             :                             " FROM auditors"
     311             :                             " WHERE"
     312             :                             "   is_active;",
     313             :                             0),
     314             :     /* Used in #postgres_get_denomination_info() */
     315         580 :     GNUNET_PQ_make_prepare ("denomination_get",
     316             :                             "SELECT"
     317             :                             " master_sig"
     318             :                             ",valid_from"
     319             :                             ",expire_withdraw"
     320             :                             ",expire_deposit"
     321             :                             ",expire_legal"
     322             :                             ",coin_val"                                            /* value of this denom */
     323             :                             ",coin_frac"                                            /* fractional value of this denom */
     324             :                             ",fee_withdraw_val"
     325             :                             ",fee_withdraw_frac"
     326             :                             ",fee_deposit_val"
     327             :                             ",fee_deposit_frac"
     328             :                             ",fee_refresh_val"
     329             :                             ",fee_refresh_frac"
     330             :                             ",fee_refund_val"
     331             :                             ",fee_refund_frac"
     332             :                             " FROM denominations"
     333             :                             " WHERE denom_pub_hash=$1;",
     334             :                             1),
     335             :     /* Used in #postgres_insert_denomination_revocation() */
     336         580 :     GNUNET_PQ_make_prepare ("denomination_revocation_insert",
     337             :                             "INSERT INTO denomination_revocations "
     338             :                             "(denominations_serial"
     339             :                             ",master_sig"
     340             :                             ") SELECT denominations_serial,$2"
     341             :                             "    FROM denominations"
     342             :                             "   WHERE denom_pub_hash=$1;",
     343             :                             2),
     344             :     /* Used in #postgres_get_denomination_revocation() */
     345         580 :     GNUNET_PQ_make_prepare ("denomination_revocation_get",
     346             :                             "SELECT"
     347             :                             " master_sig"
     348             :                             ",denom_revocations_serial_id"
     349             :                             " FROM denomination_revocations"
     350             :                             " WHERE denominations_serial="
     351             :                             "  (SELECT denominations_serial"
     352             :                             "    FROM denominations"
     353             :                             "    WHERE denom_pub_hash=$1);",
     354             :                             1),
     355             :     /* Used in #postgres_reserves_get() */
     356         580 :     GNUNET_PQ_make_prepare ("reserves_get",
     357             :                             "SELECT"
     358             :                             " current_balance_val"
     359             :                             ",current_balance_frac"
     360             :                             ",expiration_date"
     361             :                             ",gc_date"
     362             :                             " FROM reserves"
     363             :                             " WHERE reserve_pub=$1"
     364             :                             " LIMIT 1;",
     365             :                             1),
     366         580 :     GNUNET_PQ_make_prepare ("reserve_create",
     367             :                             "INSERT INTO reserves "
     368             :                             "(reserve_pub"
     369             :                             ",account_details"
     370             :                             ",current_balance_val"
     371             :                             ",current_balance_frac"
     372             :                             ",expiration_date"
     373             :                             ",gc_date"
     374             :                             ") VALUES "
     375             :                             "($1, $2, $3, $4, $5, $6)"
     376             :                             " ON CONFLICT DO NOTHING"
     377             :                             " RETURNING reserve_uuid;",
     378             :                             6),
     379             :     /* Used in #postgres_insert_reserve_closed() */
     380         580 :     GNUNET_PQ_make_prepare ("reserves_close_insert",
     381             :                             "INSERT INTO reserves_close "
     382             :                             "(reserve_uuid"
     383             :                             ",execution_date"
     384             :                             ",wtid"
     385             :                             ",receiver_account"
     386             :                             ",amount_val"
     387             :                             ",amount_frac"
     388             :                             ",closing_fee_val"
     389             :                             ",closing_fee_frac"
     390             :                             ") SELECT reserve_uuid, $2, $3, $4, $5, $6, $7, $8"
     391             :                             "  FROM reserves"
     392             :                             "  WHERE reserve_pub=$1;",
     393             :                             8),
     394             :     /* Used in #reserves_update() when the reserve is updated */
     395         580 :     GNUNET_PQ_make_prepare ("reserve_update",
     396             :                             "UPDATE reserves"
     397             :                             " SET"
     398             :                             " expiration_date=$1"
     399             :                             ",gc_date=$2"
     400             :                             ",current_balance_val=$3"
     401             :                             ",current_balance_frac=$4"
     402             :                             " WHERE reserve_pub=$5;",
     403             :                             5),
     404             :     /* Used in #postgres_reserves_in_insert() to store transaction details */
     405         580 :     GNUNET_PQ_make_prepare ("reserves_in_add_transaction",
     406             :                             "INSERT INTO reserves_in "
     407             :                             "(reserve_uuid"
     408             :                             ",wire_reference"
     409             :                             ",credit_val"
     410             :                             ",credit_frac"
     411             :                             ",exchange_account_section"
     412             :                             ",sender_account_details"
     413             :                             ",execution_date"
     414             :                             ") SELECT reserve_uuid, $2, $3, $4, $5, $6, $7"
     415             :                             "  FROM reserves"
     416             :                             "  WHERE reserve_pub=$1"
     417             :                             " ON CONFLICT DO NOTHING;",
     418             :                             7),
     419             :     /* Used in #postgres_reserves_in_insert() to store transaction details */
     420         580 :     GNUNET_PQ_make_prepare ("reserves_in_add_by_uuid",
     421             :                             "INSERT INTO reserves_in "
     422             :                             "(reserve_uuid"
     423             :                             ",wire_reference"
     424             :                             ",credit_val"
     425             :                             ",credit_frac"
     426             :                             ",exchange_account_section"
     427             :                             ",sender_account_details"
     428             :                             ",execution_date"
     429             :                             ") VALUES ($1, $2, $3, $4, $5, $6, $7)"
     430             :                             " ON CONFLICT DO NOTHING;",
     431             :                             7),
     432             :     /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
     433             :        transactions for reserves with serial id '\geq' the given parameter */
     434         580 :     GNUNET_PQ_make_prepare ("reserves_in_get_latest_wire_reference",
     435             :                             "SELECT"
     436             :                             " wire_reference"
     437             :                             " FROM reserves_in"
     438             :                             " WHERE exchange_account_section=$1"
     439             :                             " ORDER BY reserve_in_serial_id DESC"
     440             :                             " LIMIT 1;",
     441             :                             1),
     442             :     /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
     443             :        transactions for reserves with serial id '\geq' the given parameter */
     444         580 :     GNUNET_PQ_make_prepare ("audit_reserves_in_get_transactions_incr",
     445             :                             "SELECT"
     446             :                             " reserves.reserve_pub"
     447             :                             ",wire_reference"
     448             :                             ",credit_val"
     449             :                             ",credit_frac"
     450             :                             ",execution_date"
     451             :                             ",sender_account_details"
     452             :                             ",reserve_in_serial_id"
     453             :                             " FROM reserves_in"
     454             :                             " JOIN reserves"
     455             :                             "   USING (reserve_uuid)"
     456             :                             " WHERE reserve_in_serial_id>=$1"
     457             :                             " ORDER BY reserve_in_serial_id;",
     458             :                             1),
     459             :     /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
     460             :        transactions for reserves with serial id '\geq' the given parameter */
     461         580 :     GNUNET_PQ_make_prepare (
     462             :       "audit_reserves_in_get_transactions_incr_by_account",
     463             :       "SELECT"
     464             :       " reserves.reserve_pub"
     465             :       ",wire_reference"
     466             :       ",credit_val"
     467             :       ",credit_frac"
     468             :       ",execution_date"
     469             :       ",sender_account_details"
     470             :       ",reserve_in_serial_id"
     471             :       " FROM reserves_in"
     472             :       " JOIN reserves "
     473             :       "   USING (reserve_uuid)"
     474             :       " WHERE reserve_in_serial_id>=$1 AND exchange_account_section=$2"
     475             :       " ORDER BY reserve_in_serial_id;",
     476             :       2),
     477             :     /* Used in #postgres_get_reserve_history() to obtain inbound transactions
     478             :        for a reserve */
     479         580 :     GNUNET_PQ_make_prepare ("reserves_in_get_transactions",
     480             :                             "SELECT"
     481             :                             " wire_reference"
     482             :                             ",credit_val"
     483             :                             ",credit_frac"
     484             :                             ",execution_date"
     485             :                             ",sender_account_details"
     486             :                             " FROM reserves_in"
     487             :                             " WHERE reserve_uuid="
     488             :                             " (SELECT reserve_uuid "
     489             :                             "   FROM reserves"
     490             :                             "   WHERE reserve_pub=$1);",
     491             :                             1),
     492             :     /* Lock withdraw table; NOTE: we may want to eventually shard the
     493             :        deposit table to avoid this lock being the main point of
     494             :        contention limiting transaction performance. */
     495         580 :     GNUNET_PQ_make_prepare ("lock_withdraw",
     496             :                             "LOCK TABLE reserves_out;",
     497             :                             0),
     498             :     /* Used in #postgres_insert_withdraw_info() to store
     499             :        the signature of a blinded coin with the blinded coin's
     500             :        details before returning it during /reserve/withdraw. We store
     501             :        the coin's denomination information (public key, signature)
     502             :        and the blinded message as well as the reserve that the coin
     503             :        is being withdrawn from and the signature of the message
     504             :        authorizing the withdrawal. */
     505         580 :     GNUNET_PQ_make_prepare ("insert_withdraw_info",
     506             :                             "WITH ds AS"
     507             :                             " (SELECT denominations_serial"
     508             :                             "    FROM denominations"
     509             :                             "   WHERE denom_pub_hash=$2)"
     510             :                             "INSERT INTO reserves_out "
     511             :                             "(h_blind_ev"
     512             :                             ",denominations_serial"
     513             :                             ",denom_sig"
     514             :                             ",reserve_uuid"
     515             :                             ",reserve_sig"
     516             :                             ",execution_date"
     517             :                             ",amount_with_fee_val"
     518             :                             ",amount_with_fee_frac"
     519             :                             ") SELECT $1, ds.denominations_serial, $3, reserve_uuid, $5, $6, $7, $8"
     520             :                             "    FROM reserves"
     521             :                             "    CROSS JOIN ds"
     522             :                             "    WHERE reserve_pub=$4;",
     523             :                             8),
     524             :     /* Used in #postgres_get_withdraw_info() to
     525             :        locate the response for a /reserve/withdraw request
     526             :        using the hash of the blinded message.  Used to
     527             :        make sure /reserve/withdraw requests are idempotent. */
     528         580 :     GNUNET_PQ_make_prepare ("get_withdraw_info",
     529             :                             "SELECT"
     530             :                             " denom.denom_pub_hash"
     531             :                             ",denom_sig"
     532             :                             ",reserve_sig"
     533             :                             ",reserves.reserve_pub"
     534             :                             ",execution_date"
     535             :                             ",amount_with_fee_val"
     536             :                             ",amount_with_fee_frac"
     537             :                             ",denom.fee_withdraw_val"
     538             :                             ",denom.fee_withdraw_frac"
     539             :                             " FROM reserves_out"
     540             :                             "    JOIN reserves"
     541             :                             "      USING (reserve_uuid)"
     542             :                             "    JOIN denominations denom"
     543             :                             "      USING (denominations_serial)"
     544             :                             " WHERE h_blind_ev=$1;",
     545             :                             1),
     546             :     /* Used during #postgres_get_reserve_history() to
     547             :        obtain all of the /reserve/withdraw operations that
     548             :        have been performed on a given reserve. (i.e. to
     549             :        demonstrate double-spending) */
     550         580 :     GNUNET_PQ_make_prepare ("get_reserves_out",
     551             :                             "SELECT"
     552             :                             " h_blind_ev"
     553             :                             ",denom.denom_pub_hash"
     554             :                             ",denom_sig"
     555             :                             ",reserve_sig"
     556             :                             ",execution_date"
     557             :                             ",amount_with_fee_val"
     558             :                             ",amount_with_fee_frac"
     559             :                             ",denom.fee_withdraw_val"
     560             :                             ",denom.fee_withdraw_frac"
     561             :                             " FROM reserves_out"
     562             :                             "    JOIN denominations denom"
     563             :                             "      USING (denominations_serial)"
     564             :                             " WHERE reserve_uuid="
     565             :                             "   (SELECT reserve_uuid"
     566             :                             "      FROM reserves"
     567             :                             "     WHERE reserve_pub=$1);",
     568             :                             1),
     569             :     /* Used in #postgres_select_withdrawals_above_serial_id() */
     570         580 :     GNUNET_PQ_make_prepare ("audit_get_reserves_out_incr",
     571             :                             "SELECT"
     572             :                             " h_blind_ev"
     573             :                             ",denom.denom_pub"
     574             :                             ",reserve_sig"
     575             :                             ",reserves.reserve_pub"
     576             :                             ",execution_date"
     577             :                             ",amount_with_fee_val"
     578             :                             ",amount_with_fee_frac"
     579             :                             ",reserve_out_serial_id"
     580             :                             " FROM reserves_out"
     581             :                             "    JOIN reserves"
     582             :                             "      USING (reserve_uuid)"
     583             :                             "    JOIN denominations denom"
     584             :                             "      USING (denominations_serial)"
     585             :                             " WHERE reserve_out_serial_id>=$1"
     586             :                             " ORDER BY reserve_out_serial_id ASC;",
     587             :                             1),
     588             : 
     589             :     /* Used in #postgres_count_known_coins() */
     590         580 :     GNUNET_PQ_make_prepare ("count_known_coins",
     591             :                             "SELECT"
     592             :                             " COUNT(*) AS count"
     593             :                             " FROM known_coins"
     594             :                             " WHERE denominations_serial="
     595             :                             "  (SELECT denominations_serial"
     596             :                             "    FROM denominations"
     597             :                             "    WHERE denom_pub_hash=$1);",
     598             :                             1),
     599             :     /* Used in #postgres_get_known_coin() to fetch
     600             :        the denomination public key and signature for
     601             :        a coin known to the exchange. */
     602         580 :     GNUNET_PQ_make_prepare ("get_known_coin",
     603             :                             "SELECT"
     604             :                             " denominations.denom_pub_hash"
     605             :                             ",denom_sig"
     606             :                             " FROM known_coins"
     607             :                             " JOIN denominations USING (denominations_serial)"
     608             :                             " WHERE coin_pub=$1;",
     609             :                             1),
     610             :     /* Used in #postgres_ensure_coin_known() */
     611         580 :     GNUNET_PQ_make_prepare ("get_known_coin_dh",
     612             :                             "SELECT"
     613             :                             " denominations.denom_pub_hash"
     614             :                             " FROM known_coins"
     615             :                             " JOIN denominations USING (denominations_serial)"
     616             :                             " WHERE coin_pub=$1;",
     617             :                             1),
     618             :     /* Used in #postgres_get_coin_denomination() to fetch
     619             :        the denomination public key hash for
     620             :        a coin known to the exchange. */
     621         580 :     GNUNET_PQ_make_prepare ("get_coin_denomination",
     622             :                             "SELECT"
     623             :                             " denominations.denom_pub_hash"
     624             :                             " FROM known_coins"
     625             :                             " JOIN denominations USING (denominations_serial)"
     626             :                             " WHERE coin_pub=$1"
     627             :                             " FOR SHARE;",
     628             :                             1),
     629             :     /* Lock deposit table; NOTE: we may want to eventually shard the
     630             :        deposit table to avoid this lock being the main point of
     631             :        contention limiting transaction performance. */
     632         580 :     GNUNET_PQ_make_prepare ("lock_known_coins",
     633             :                             "LOCK TABLE known_coins;",
     634             :                             0),
     635             :     /* Used in #postgres_insert_known_coin() to store
     636             :        the denomination public key and signature for
     637             :        a coin known to the exchange. */
     638         580 :     GNUNET_PQ_make_prepare ("insert_known_coin",
     639             :                             "INSERT INTO known_coins "
     640             :                             "(coin_pub"
     641             :                             ",denominations_serial"
     642             :                             ",denom_sig"
     643             :                             ") SELECT $1, denominations_serial, $3 "
     644             :                             "    FROM denominations"
     645             :                             "   WHERE denom_pub_hash=$2;",
     646             :                             3),
     647             : 
     648             :     /* Used in #postgres_insert_melt() to store
     649             :        high-level information about a melt operation */
     650         580 :     GNUNET_PQ_make_prepare ("insert_melt",
     651             :                             "INSERT INTO refresh_commitments "
     652             :                             "(rc "
     653             :                             ",old_known_coin_id "
     654             :                             ",old_coin_sig "
     655             :                             ",amount_with_fee_val "
     656             :                             ",amount_with_fee_frac "
     657             :                             ",noreveal_index "
     658             :                             ") SELECT $1, known_coin_id, $3, $4, $5, $6"
     659             :                             "    FROM known_coins"
     660             :                             "   WHERE coin_pub=$2",
     661             :                             6),
     662             :     /* Used in #postgres_get_melt() to fetch
     663             :        high-level information about a melt operation */
     664         580 :     GNUNET_PQ_make_prepare ("get_melt",
     665             :                             "SELECT"
     666             :                             " denoms.denom_pub_hash"
     667             :                             ",denoms.fee_refresh_val"
     668             :                             ",denoms.fee_refresh_frac"
     669             :                             ",kc.coin_pub AS old_coin_pub"
     670             :                             ",old_coin_sig"
     671             :                             ",amount_with_fee_val"
     672             :                             ",amount_with_fee_frac"
     673             :                             ",noreveal_index"
     674             :                             " FROM refresh_commitments"
     675             :                             "   JOIN known_coins kc"
     676             :                             "     ON (refresh_commitments.old_known_coin_id = kc.known_coin_id)"
     677             :                             "   JOIN denominations denoms"
     678             :                             "     ON (kc.denominations_serial = denoms.denominations_serial)"
     679             :                             " WHERE rc=$1;",
     680             :                             1),
     681             :     /* Used in #postgres_get_melt_index() to fetch
     682             :        the noreveal index from a previous melt operation */
     683         580 :     GNUNET_PQ_make_prepare ("get_melt_index",
     684             :                             "SELECT"
     685             :                             " noreveal_index"
     686             :                             " FROM refresh_commitments"
     687             :                             " WHERE rc=$1;",
     688             :                             1),
     689             :     /* Used in #postgres_select_refreshes_above_serial_id() to fetch
     690             :        refresh session with id '\geq' the given parameter */
     691         580 :     GNUNET_PQ_make_prepare ("audit_get_refresh_commitments_incr",
     692             :                             "SELECT"
     693             :                             " denom.denom_pub"
     694             :                             ",kc.coin_pub AS old_coin_pub"
     695             :                             ",old_coin_sig"
     696             :                             ",amount_with_fee_val"
     697             :                             ",amount_with_fee_frac"
     698             :                             ",noreveal_index"
     699             :                             ",melt_serial_id"
     700             :                             ",rc"
     701             :                             " FROM refresh_commitments"
     702             :                             "   JOIN known_coins kc"
     703             :                             "     ON (refresh_commitments.old_known_coin_id = kc.known_coin_id)"
     704             :                             "   JOIN denominations denom"
     705             :                             "     ON (kc.denominations_serial = denom.denominations_serial)"
     706             :                             " WHERE melt_serial_id>=$1"
     707             :                             " ORDER BY melt_serial_id ASC;",
     708             :                             1),
     709             :     /* Query the 'refresh_commitments' by coin public key */
     710         580 :     GNUNET_PQ_make_prepare ("get_refresh_session_by_coin",
     711             :                             "SELECT"
     712             :                             " rc"
     713             :                             ",old_coin_sig"
     714             :                             ",amount_with_fee_val"
     715             :                             ",amount_with_fee_frac"
     716             :                             ",denoms.denom_pub_hash"
     717             :                             ",denoms.fee_refresh_val"
     718             :                             ",denoms.fee_refresh_frac"
     719             :                             ",melt_serial_id"
     720             :                             " FROM refresh_commitments"
     721             :                             " JOIN known_coins kc"
     722             :                             "   ON (refresh_commitments.old_known_coin_id = kc.known_coin_id)"
     723             :                             " JOIN denominations denoms"
     724             :                             "   USING (denominations_serial)"
     725             :                             " WHERE old_known_coin_id="
     726             :                             "(SELECT known_coin_id"
     727             :                             "   FROM known_coins"
     728             :                             "  WHERE coin_pub=$1);",
     729             :                             1),
     730             :     /* Store information about the desired denominations for a
     731             :        refresh operation, used in #postgres_insert_refresh_reveal() */
     732         580 :     GNUNET_PQ_make_prepare ("insert_refresh_revealed_coin",
     733             :                             "WITH rcx AS"
     734             :                             " (SELECT melt_serial_id"
     735             :                             "    FROM refresh_commitments"
     736             :                             "   WHERE rc=$1)"
     737             :                             "INSERT INTO refresh_revealed_coins "
     738             :                             "(melt_serial_id "
     739             :                             ",freshcoin_index "
     740             :                             ",link_sig "
     741             :                             ",denominations_serial "
     742             :                             ",coin_ev"
     743             :                             ",h_coin_ev"
     744             :                             ",ev_sig"
     745             :                             ") SELECT rcx.melt_serial_id, $2, $3, "
     746             :                             "         denominations_serial, $5, $6, $7"
     747             :                             "    FROM denominations"
     748             :                             "   CROSS JOIN rcx"
     749             :                             "   WHERE denom_pub_hash=$4;",
     750             :                             7),
     751             :     /* Obtain information about the coins created in a refresh
     752             :        operation, used in #postgres_get_refresh_reveal() */
     753         580 :     GNUNET_PQ_make_prepare ("get_refresh_revealed_coins",
     754             :                             "SELECT "
     755             :                             " freshcoin_index"
     756             :                             ",denom.denom_pub"
     757             :                             ",link_sig"
     758             :                             ",coin_ev"
     759             :                             ",ev_sig"
     760             :                             " FROM refresh_revealed_coins"
     761             :                             "    JOIN denominations denom "
     762             :                             "      USING (denominations_serial)"
     763             :                             "    JOIN refresh_commitments"
     764             :                             "      USING (melt_serial_id)"
     765             :                             " WHERE rc=$1"
     766             :                             "   ORDER BY freshcoin_index ASC;",
     767             :                             1),
     768             : 
     769             :     /* Used in #postgres_insert_refresh_reveal() to store the transfer
     770             :        keys we learned */
     771         580 :     GNUNET_PQ_make_prepare ("insert_refresh_transfer_keys",
     772             :                             "INSERT INTO refresh_transfer_keys "
     773             :                             "(melt_serial_id"
     774             :                             ",transfer_pub"
     775             :                             ",transfer_privs"
     776             :                             ") SELECT melt_serial_id, $2, $3"
     777             :                             "    FROM refresh_commitments"
     778             :                             "   WHERE rc=$1",
     779             :                             3),
     780             :     /* Used in #postgres_get_refresh_reveal() to retrieve transfer
     781             :        keys from /refresh/reveal */
     782         580 :     GNUNET_PQ_make_prepare ("get_refresh_transfer_keys",
     783             :                             "SELECT"
     784             :                             " transfer_pub"
     785             :                             ",transfer_privs"
     786             :                             " FROM refresh_transfer_keys"
     787             :                             " JOIN refresh_commitments"
     788             :                             "   USING (melt_serial_id)"
     789             :                             " WHERE rc=$1;",
     790             :                             1),
     791             :     /* Used in #postgres_insert_refund() to store refund information */
     792         580 :     GNUNET_PQ_make_prepare ("insert_refund",
     793             :                             "INSERT INTO refunds "
     794             :                             "(deposit_serial_id "
     795             :                             ",merchant_sig "
     796             :                             ",rtransaction_id "
     797             :                             ",amount_with_fee_val "
     798             :                             ",amount_with_fee_frac "
     799             :                             ") SELECT deposit_serial_id, $3, $5, $6, $7"
     800             :                             "    FROM deposits"
     801             :                             "    JOIN known_coins USING (known_coin_id)"
     802             :                             "   WHERE coin_pub=$1"
     803             :                             "     AND h_contract_terms=$4"
     804             :                             "     AND merchant_pub=$2",
     805             :                             7),
     806             :     /* Query the 'refunds' by coin public key */
     807         580 :     GNUNET_PQ_make_prepare ("get_refunds_by_coin",
     808             :                             "SELECT"
     809             :                             " merchant_pub"
     810             :                             ",merchant_sig"
     811             :                             ",h_contract_terms"
     812             :                             ",rtransaction_id"
     813             :                             ",refunds.amount_with_fee_val"
     814             :                             ",refunds.amount_with_fee_frac"
     815             :                             ",denom.fee_refund_val "
     816             :                             ",denom.fee_refund_frac "
     817             :                             ",refund_serial_id"
     818             :                             " FROM refunds"
     819             :                             " JOIN deposits USING (deposit_serial_id)"
     820             :                             " JOIN known_coins USING (known_coin_id)"
     821             :                             " JOIN denominations denom USING (denominations_serial)"
     822             :                             " WHERE coin_pub=$1;",
     823             :                             1),
     824             :     /* Query the 'refunds' by coin public key, merchant_pub and contract hash */
     825         580 :     GNUNET_PQ_make_prepare ("get_refunds_by_coin_and_contract",
     826             :                             "SELECT"
     827             :                             " refunds.amount_with_fee_val"
     828             :                             ",refunds.amount_with_fee_frac"
     829             :                             " FROM refunds"
     830             :                             " JOIN deposits USING (deposit_serial_id)"
     831             :                             " JOIN known_coins USING (known_coin_id)"
     832             :                             " WHERE coin_pub=$1"
     833             :                             "   AND merchant_pub=$2"
     834             :                             "   AND h_contract_terms=$3;",
     835             :                             3),
     836             :     /* Fetch refunds with rowid '\geq' the given parameter */
     837         580 :     GNUNET_PQ_make_prepare ("audit_get_refunds_incr",
     838             :                             "SELECT"
     839             :                             " merchant_pub"
     840             :                             ",merchant_sig"
     841             :                             ",h_contract_terms"
     842             :                             ",rtransaction_id"
     843             :                             ",denom.denom_pub"
     844             :                             ",kc.coin_pub"
     845             :                             ",refunds.amount_with_fee_val"
     846             :                             ",refunds.amount_with_fee_frac"
     847             :                             ",refund_serial_id"
     848             :                             " FROM refunds"
     849             :                             "   JOIN deposits USING (deposit_serial_id)"
     850             :                             "   JOIN known_coins kc USING (known_coin_id)"
     851             :                             "   JOIN denominations denom ON (kc.denominations_serial = denom.denominations_serial)"
     852             :                             " WHERE refund_serial_id>=$1"
     853             :                             " ORDER BY refund_serial_id ASC;",
     854             :                             1),
     855             :     /* Lock deposit table; NOTE: we may want to eventually shard the
     856             :        deposit table to avoid this lock being the main point of
     857             :        contention limiting transaction performance. */
     858         580 :     GNUNET_PQ_make_prepare ("lock_deposit",
     859             :                             "LOCK TABLE deposits;",
     860             :                             0),
     861             :     /* Store information about a /deposit the exchange is to execute.
     862             :        Used in #postgres_insert_deposit(). */
     863         580 :     GNUNET_PQ_make_prepare ("insert_deposit",
     864             :                             "INSERT INTO deposits "
     865             :                             "(known_coin_id"
     866             :                             ",amount_with_fee_val"
     867             :                             ",amount_with_fee_frac"
     868             :                             ",wallet_timestamp"
     869             :                             ",refund_deadline"
     870             :                             ",wire_deadline"
     871             :                             ",merchant_pub"
     872             :                             ",h_contract_terms"
     873             :                             ",h_wire"
     874             :                             ",coin_sig"
     875             :                             ",wire"
     876             :                             ",exchange_timestamp"
     877             :                             ") SELECT known_coin_id, $2, $3, $4, $5, $6, "
     878             :                             " $7, $8, $9, $10, $11, $12"
     879             :                             "    FROM known_coins"
     880             :                             "   WHERE coin_pub=$1;",
     881             :                             12),
     882             :     /* Fetch an existing deposit request, used to ensure idempotency
     883             :        during /deposit processing. Used in #postgres_have_deposit(). */
     884         580 :     GNUNET_PQ_make_prepare ("get_deposit",
     885             :                             "SELECT"
     886             :                             " amount_with_fee_val"
     887             :                             ",amount_with_fee_frac"
     888             :                             ",denominations.fee_deposit_val"
     889             :                             ",denominations.fee_deposit_frac"
     890             :                             ",wallet_timestamp"
     891             :                             ",exchange_timestamp"
     892             :                             ",refund_deadline"
     893             :                             ",wire_deadline"
     894             :                             ",h_contract_terms"
     895             :                             ",h_wire"
     896             :                             " FROM deposits"
     897             :                             " JOIN known_coins USING (known_coin_id)"
     898             :                             " JOIN denominations USING (denominations_serial)"
     899             :                             " WHERE ((coin_pub=$1)"
     900             :                             "    AND (merchant_pub=$3)"
     901             :                             "    AND (h_contract_terms=$2));",
     902             :                             3),
     903             :     /* Fetch deposits with rowid '\geq' the given parameter */
     904         580 :     GNUNET_PQ_make_prepare ("audit_get_deposits_incr",
     905             :                             "SELECT"
     906             :                             " amount_with_fee_val"
     907             :                             ",amount_with_fee_frac"
     908             :                             ",wallet_timestamp"
     909             :                             ",exchange_timestamp"
     910             :                             ",merchant_pub"
     911             :                             ",denom.denom_pub"
     912             :                             ",kc.coin_pub"
     913             :                             ",coin_sig"
     914             :                             ",refund_deadline"
     915             :                             ",wire_deadline"
     916             :                             ",h_contract_terms"
     917             :                             ",wire"
     918             :                             ",done"
     919             :                             ",deposit_serial_id"
     920             :                             " FROM deposits"
     921             :                             "    JOIN known_coins kc USING (known_coin_id)"
     922             :                             "    JOIN denominations denom USING (denominations_serial)"
     923             :                             " WHERE ("
     924             :                             "  (deposit_serial_id>=$1)"
     925             :                             " )"
     926             :                             " ORDER BY deposit_serial_id ASC;",
     927             :                             1),
     928             :     /* Fetch an existing deposit request.
     929             :        Used in #postgres_lookup_transfer_by_deposit(). */
     930         580 :     GNUNET_PQ_make_prepare ("get_deposit_for_wtid",
     931             :                             "SELECT"
     932             :                             " amount_with_fee_val"
     933             :                             ",amount_with_fee_frac"
     934             :                             ",denom.fee_deposit_val"
     935             :                             ",denom.fee_deposit_frac"
     936             :                             ",wire_deadline"
     937             :                             " FROM deposits"
     938             :                             "    JOIN known_coins USING (known_coin_id)"
     939             :                             "    JOIN denominations denom USING (denominations_serial)"
     940             :                             " WHERE ((coin_pub=$1)"
     941             :                             "    AND (merchant_pub=$2)"
     942             :                             "    AND (h_contract_terms=$3)"
     943             :                             "    AND (h_wire=$4)"
     944             :                             " );",
     945             :                             4),
     946             :     /* Used in #postgres_get_ready_deposit() */
     947         580 :     GNUNET_PQ_make_prepare ("deposits_get_ready",
     948             :                             "SELECT"
     949             :                             " deposit_serial_id"
     950             :                             ",amount_with_fee_val"
     951             :                             ",amount_with_fee_frac"
     952             :                             ",denom.fee_deposit_val"
     953             :                             ",denom.fee_deposit_frac"
     954             :                             ",wire_deadline"
     955             :                             ",h_contract_terms"
     956             :                             ",wire"
     957             :                             ",merchant_pub"
     958             :                             ",kc.coin_pub"
     959             :                             ",exchange_timestamp"
     960             :                             ",wallet_timestamp"
     961             :                             " FROM deposits"
     962             :                             "    JOIN known_coins kc USING (known_coin_id)"
     963             :                             "    JOIN denominations denom USING (denominations_serial)"
     964             :                             " WHERE tiny=FALSE"
     965             :                             "    AND done=FALSE"
     966             :                             "    AND wire_deadline<=$1"
     967             :                             "    AND refund_deadline<$1"
     968             :                             " ORDER BY wire_deadline ASC"
     969             :                             " LIMIT 1;",
     970             :                             1),
     971             :     /* Used in #postgres_iterate_matching_deposits() */
     972         580 :     GNUNET_PQ_make_prepare ("deposits_iterate_matching",
     973             :                             "SELECT"
     974             :                             " deposit_serial_id"
     975             :                             ",amount_with_fee_val"
     976             :                             ",amount_with_fee_frac"
     977             :                             ",denom.fee_deposit_val"
     978             :                             ",denom.fee_deposit_frac"
     979             :                             ",h_contract_terms"
     980             :                             ",kc.coin_pub"
     981             :                             " FROM deposits"
     982             :                             "    JOIN known_coins kc USING (known_coin_id)"
     983             :                             "    JOIN denominations denom USING (denominations_serial)"
     984             :                             " WHERE"
     985             :                             "     merchant_pub=$1 AND"
     986             :                             "     h_wire=$2 AND"
     987             :                             "     done=FALSE"
     988             :                             " ORDER BY wire_deadline ASC"
     989             :                             " LIMIT "
     990             :                             TALER_QUOTE (
     991             :                               TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT) ";",
     992             :                             2),
     993             :     /* Used in #postgres_mark_deposit_tiny() */
     994         580 :     GNUNET_PQ_make_prepare ("mark_deposit_tiny",
     995             :                             "UPDATE deposits"
     996             :                             " SET tiny=TRUE"
     997             :                             " WHERE deposit_serial_id=$1",
     998             :                             1),
     999             :     /* Used in #postgres_mark_deposit_done() */
    1000         580 :     GNUNET_PQ_make_prepare ("mark_deposit_done",
    1001             :                             "UPDATE deposits"
    1002             :                             " SET done=TRUE"
    1003             :                             " WHERE deposit_serial_id=$1;",
    1004             :                             1),
    1005             :     /* Used in #postgres_test_deposit_done() */
    1006         580 :     GNUNET_PQ_make_prepare ("test_deposit_done",
    1007             :                             "SELECT done"
    1008             :                             " FROM deposits"
    1009             :                             " JOIN known_coins USING (known_coin_id)"
    1010             :                             " WHERE coin_pub=$1"
    1011             :                             "   AND merchant_pub=$2"
    1012             :                             "   AND h_contract_terms=$3"
    1013             :                             "   AND h_wire=$4;",
    1014             :                             5),
    1015             :     /* Used in #postgres_get_coin_transactions() to obtain information
    1016             :        about how a coin has been spend with /deposit requests. */
    1017         580 :     GNUNET_PQ_make_prepare ("get_deposit_with_coin_pub",
    1018             :                             "SELECT"
    1019             :                             " amount_with_fee_val"
    1020             :                             ",amount_with_fee_frac"
    1021             :                             ",denoms.fee_deposit_val"
    1022             :                             ",denoms.fee_deposit_frac"
    1023             :                             ",denoms.denom_pub_hash"
    1024             :                             ",wallet_timestamp"
    1025             :                             ",refund_deadline"
    1026             :                             ",wire_deadline"
    1027             :                             ",merchant_pub"
    1028             :                             ",h_contract_terms"
    1029             :                             ",h_wire"
    1030             :                             ",wire"
    1031             :                             ",coin_sig"
    1032             :                             ",deposit_serial_id"
    1033             :                             ",done"
    1034             :                             " FROM deposits"
    1035             :                             "    JOIN known_coins kc"
    1036             :                             "      USING (known_coin_id)"
    1037             :                             "    JOIN denominations denoms"
    1038             :                             "      USING (denominations_serial)"
    1039             :                             " WHERE coin_pub=$1;",
    1040             :                             1),
    1041             : 
    1042             :     /* Used in #postgres_get_link_data(). */
    1043         580 :     GNUNET_PQ_make_prepare ("get_link",
    1044             :                             "SELECT "
    1045             :                             " tp.transfer_pub"
    1046             :                             ",denoms.denom_pub"
    1047             :                             ",rrc.ev_sig"
    1048             :                             ",rrc.link_sig"
    1049             :                             " FROM refresh_commitments"
    1050             :                             "     JOIN refresh_revealed_coins rrc"
    1051             :                             "       USING (melt_serial_id)"
    1052             :                             "     JOIN refresh_transfer_keys tp"
    1053             :                             "       USING (melt_serial_id)"
    1054             :                             "     JOIN denominations denoms"
    1055             :                             "       ON (rrc.denominations_serial = denoms.denominations_serial)"
    1056             :                             " WHERE old_known_coin_id="
    1057             :                             "   (SELECT known_coin_id "
    1058             :                             "      FROM known_coins"
    1059             :                             "     WHERE coin_pub=$1)"
    1060             :                             " ORDER BY tp.transfer_pub, rrc.freshcoin_index ASC",
    1061             :                             1),
    1062             :     /* Used in #postgres_lookup_wire_transfer */
    1063         580 :     GNUNET_PQ_make_prepare ("lookup_transactions",
    1064             :                             "SELECT"
    1065             :                             " aggregation_serial_id"
    1066             :                             ",deposits.h_contract_terms"
    1067             :                             ",deposits.wire"
    1068             :                             ",deposits.h_wire"
    1069             :                             ",kc.coin_pub"
    1070             :                             ",deposits.merchant_pub"
    1071             :                             ",wire_out.execution_date"
    1072             :                             ",deposits.amount_with_fee_val"
    1073             :                             ",deposits.amount_with_fee_frac"
    1074             :                             ",denom.fee_deposit_val"
    1075             :                             ",denom.fee_deposit_frac"
    1076             :                             ",denom.denom_pub"
    1077             :                             " FROM aggregation_tracking"
    1078             :                             "    JOIN deposits"
    1079             :                             "      USING (deposit_serial_id)"
    1080             :                             "    JOIN known_coins kc"
    1081             :                             "      USING (known_coin_id)"
    1082             :                             "    JOIN denominations denom"
    1083             :                             "      USING (denominations_serial)"
    1084             :                             "    JOIN wire_out"
    1085             :                             "      USING (wtid_raw)"
    1086             :                             " WHERE wtid_raw=$1;",
    1087             :                             1),
    1088             :     /* Used in #postgres_lookup_transfer_by_deposit */
    1089         580 :     GNUNET_PQ_make_prepare ("lookup_deposit_wtid",
    1090             :                             "SELECT"
    1091             :                             " aggregation_tracking.wtid_raw"
    1092             :                             ",wire_out.execution_date"
    1093             :                             ",amount_with_fee_val"
    1094             :                             ",amount_with_fee_frac"
    1095             :                             ",denom.fee_deposit_val"
    1096             :                             ",denom.fee_deposit_frac"
    1097             :                             " FROM deposits"
    1098             :                             "    JOIN aggregation_tracking"
    1099             :                             "      USING (deposit_serial_id)"
    1100             :                             "    JOIN known_coins"
    1101             :                             "      USING (known_coin_id)"
    1102             :                             "    JOIN denominations denom"
    1103             :                             "      USING (denominations_serial)"
    1104             :                             "    JOIN wire_out"
    1105             :                             "      USING (wtid_raw)"
    1106             :                             " WHERE coin_pub=$1"
    1107             :                             "  AND h_contract_terms=$2"
    1108             :                             "  AND h_wire=$3"
    1109             :                             "  AND merchant_pub=$4;",
    1110             :                             4),
    1111             :     /* Used in #postgres_insert_aggregation_tracking */
    1112         580 :     GNUNET_PQ_make_prepare ("insert_aggregation_tracking",
    1113             :                             "INSERT INTO aggregation_tracking "
    1114             :                             "(deposit_serial_id"
    1115             :                             ",wtid_raw"
    1116             :                             ") VALUES "
    1117             :                             "($1, $2);",
    1118             :                             2),
    1119             :     /* Used in #postgres_get_wire_fee() */
    1120         580 :     GNUNET_PQ_make_prepare ("get_wire_fee",
    1121             :                             "SELECT "
    1122             :                             " start_date"
    1123             :                             ",end_date"
    1124             :                             ",wire_fee_val"
    1125             :                             ",wire_fee_frac"
    1126             :                             ",closing_fee_val"
    1127             :                             ",closing_fee_frac"
    1128             :                             ",master_sig"
    1129             :                             " FROM wire_fee"
    1130             :                             " WHERE wire_method=$1"
    1131             :                             "   AND start_date <= $2"
    1132             :                             "   AND end_date > $2;",
    1133             :                             2),
    1134             :     /* Used in #postgres_insert_wire_fee */
    1135         580 :     GNUNET_PQ_make_prepare ("insert_wire_fee",
    1136             :                             "INSERT INTO wire_fee "
    1137             :                             "(wire_method"
    1138             :                             ",start_date"
    1139             :                             ",end_date"
    1140             :                             ",wire_fee_val"
    1141             :                             ",wire_fee_frac"
    1142             :                             ",closing_fee_val"
    1143             :                             ",closing_fee_frac"
    1144             :                             ",master_sig"
    1145             :                             ") VALUES "
    1146             :                             "($1, $2, $3, $4, $5, $6, $7, $8);",
    1147             :                             8),
    1148             :     /* Used in #postgres_store_wire_transfer_out */
    1149         580 :     GNUNET_PQ_make_prepare ("insert_wire_out",
    1150             :                             "INSERT INTO wire_out "
    1151             :                             "(execution_date"
    1152             :                             ",wtid_raw"
    1153             :                             ",wire_target"
    1154             :                             ",exchange_account_section"
    1155             :                             ",amount_val"
    1156             :                             ",amount_frac"
    1157             :                             ") VALUES "
    1158             :                             "($1, $2, $3, $4, $5, $6);",
    1159             :                             6),
    1160             :     /* Used in #postgres_wire_prepare_data_insert() to store
    1161             :        wire transfer information before actually committing it with the bank */
    1162         580 :     GNUNET_PQ_make_prepare ("wire_prepare_data_insert",
    1163             :                             "INSERT INTO prewire "
    1164             :                             "(type"
    1165             :                             ",buf"
    1166             :                             ") VALUES "
    1167             :                             "($1, $2);",
    1168             :                             2),
    1169             :     /* Used in #postgres_wire_prepare_data_mark_finished() */
    1170         580 :     GNUNET_PQ_make_prepare ("wire_prepare_data_mark_done",
    1171             :                             "UPDATE prewire"
    1172             :                             " SET finished=TRUE"
    1173             :                             " WHERE prewire_uuid=$1;",
    1174             :                             1),
    1175             :     /* Used in #postgres_wire_prepare_data_mark_failed() */
    1176         580 :     GNUNET_PQ_make_prepare ("wire_prepare_data_mark_failed",
    1177             :                             "UPDATE prewire"
    1178             :                             " SET failed=TRUE"
    1179             :                             " WHERE prewire_uuid=$1;",
    1180             :                             1),
    1181             :     /* Used in #postgres_wire_prepare_data_get() */
    1182         580 :     GNUNET_PQ_make_prepare ("wire_prepare_data_get",
    1183             :                             "SELECT"
    1184             :                             " prewire_uuid"
    1185             :                             ",type"
    1186             :                             ",buf"
    1187             :                             " FROM prewire"
    1188             :                             " WHERE finished=FALSE"
    1189             :                             "   AND failed=FALSE"
    1190             :                             " ORDER BY prewire_uuid ASC"
    1191             :                             " LIMIT 1;",
    1192             :                             0),
    1193             :     /* Used in #postgres_select_deposits_missing_wire */
    1194         580 :     GNUNET_PQ_make_prepare ("deposits_get_overdue",
    1195             :                             "SELECT"
    1196             :                             " deposit_serial_id"
    1197             :                             ",coin_pub"
    1198             :                             ",amount_with_fee_val"
    1199             :                             ",amount_with_fee_frac"
    1200             :                             ",wire"
    1201             :                             ",wire_deadline"
    1202             :                             ",tiny"
    1203             :                             ",done"
    1204             :                             " FROM deposits d"
    1205             :                             " JOIN known_coins USING (known_coin_id)"
    1206             :                             " WHERE wire_deadline >= $1"
    1207             :                             " AND wire_deadline < $2"
    1208             :                             " AND NOT (EXISTS (SELECT 1"
    1209             :                             "            FROM refunds"
    1210             :                             "            JOIN deposits dx USING (deposit_serial_id)"
    1211             :                             "            WHERE (dx.known_coin_id = d.known_coin_id))"
    1212             :                             "       OR EXISTS (SELECT 1"
    1213             :                             "            FROM aggregation_tracking"
    1214             :                             "            WHERE (aggregation_tracking.deposit_serial_id = d.deposit_serial_id)))"
    1215             :                             " ORDER BY wire_deadline ASC",
    1216             :                             2),
    1217             :     /* Used in #postgres_select_wire_out_above_serial_id() */
    1218         580 :     GNUNET_PQ_make_prepare ("audit_get_wire_incr",
    1219             :                             "SELECT"
    1220             :                             " wireout_uuid"
    1221             :                             ",execution_date"
    1222             :                             ",wtid_raw"
    1223             :                             ",wire_target"
    1224             :                             ",amount_val"
    1225             :                             ",amount_frac"
    1226             :                             " FROM wire_out"
    1227             :                             " WHERE wireout_uuid>=$1"
    1228             :                             " ORDER BY wireout_uuid ASC;",
    1229             :                             1),
    1230             :     /* Used in #postgres_select_wire_out_above_serial_id_by_account() */
    1231         580 :     GNUNET_PQ_make_prepare ("audit_get_wire_incr_by_account",
    1232             :                             "SELECT"
    1233             :                             " wireout_uuid"
    1234             :                             ",execution_date"
    1235             :                             ",wtid_raw"
    1236             :                             ",wire_target"
    1237             :                             ",amount_val"
    1238             :                             ",amount_frac"
    1239             :                             " FROM wire_out"
    1240             :                             " WHERE wireout_uuid>=$1 AND exchange_account_section=$2"
    1241             :                             " ORDER BY wireout_uuid ASC;",
    1242             :                             2),
    1243             :     /* Used in #postgres_insert_recoup_request() to store recoup
    1244             :        information */
    1245         580 :     GNUNET_PQ_make_prepare ("recoup_insert",
    1246             :                             "WITH rx AS"
    1247             :                             " (SELECT reserve_out_serial_id"
    1248             :                             "    FROM reserves_out"
    1249             :                             "   WHERE h_blind_ev=$7)"
    1250             :                             "INSERT INTO recoup "
    1251             :                             "(known_coin_id"
    1252             :                             ",coin_sig"
    1253             :                             ",coin_blind"
    1254             :                             ",amount_val"
    1255             :                             ",amount_frac"
    1256             :                             ",timestamp"
    1257             :                             ",reserve_out_serial_id"
    1258             :                             ") SELECT known_coin_id, $2, $3, $4, $5, $6, rx.reserve_out_serial_id"
    1259             :                             "    FROM known_coins"
    1260             :                             "   CROSS JOIN rx"
    1261             :                             "   WHERE coin_pub=$1;",
    1262             :                             7),
    1263             :     /* Used in #postgres_insert_recoup_refresh_request() to store recoup-refresh
    1264             :        information */
    1265         580 :     GNUNET_PQ_make_prepare ("recoup_refresh_insert",
    1266             :                             "WITH rrx AS"
    1267             :                             " (SELECT rrc_serial"
    1268             :                             "    FROM refresh_revealed_coins"
    1269             :                             "   WHERE h_coin_ev=$7)"
    1270             :                             "INSERT INTO recoup_refresh "
    1271             :                             "(known_coin_id"
    1272             :                             ",coin_sig"
    1273             :                             ",coin_blind"
    1274             :                             ",amount_val"
    1275             :                             ",amount_frac"
    1276             :                             ",timestamp"
    1277             :                             ",rrc_serial"
    1278             :                             ") SELECT known_coin_id, $2, $3, $4, $5, $6, rrx.rrc_serial"
    1279             :                             "    FROM known_coins"
    1280             :                             "   CROSS JOIN rrx"
    1281             :                             "   WHERE coin_pub=$1;",
    1282             :                             7),
    1283             :     /* Used in #postgres_select_recoup_above_serial_id() to obtain recoup transactions */
    1284         580 :     GNUNET_PQ_make_prepare ("recoup_get_incr",
    1285             :                             "SELECT"
    1286             :                             " recoup_uuid"
    1287             :                             ",timestamp"
    1288             :                             ",reserves.reserve_pub"
    1289             :                             ",coins.coin_pub"
    1290             :                             ",coin_sig"
    1291             :                             ",coin_blind"
    1292             :                             ",ro.h_blind_ev"
    1293             :                             ",denoms.denom_pub_hash"
    1294             :                             ",coins.denom_sig"
    1295             :                             ",denoms.denom_pub"
    1296             :                             ",amount_val"
    1297             :                             ",amount_frac"
    1298             :                             " FROM recoup"
    1299             :                             "    JOIN known_coins coins"
    1300             :                             "      USING (known_coin_id)"
    1301             :                             "    JOIN reserves_out ro"
    1302             :                             "      USING (reserve_out_serial_id)"
    1303             :                             "    JOIN reserves"
    1304             :                             "      USING (reserve_uuid)"
    1305             :                             "    JOIN denominations denoms"
    1306             :                             "      ON (coins.denominations_serial = denoms.denominations_serial)"
    1307             :                             " WHERE recoup_uuid>=$1"
    1308             :                             " ORDER BY recoup_uuid ASC;",
    1309             :                             1),
    1310             :     /* Used in #postgres_select_recoup_refresh_above_serial_id() to obtain
    1311             :        recoup-refresh transactions */
    1312         580 :     GNUNET_PQ_make_prepare ("recoup_refresh_get_incr",
    1313             :                             "SELECT"
    1314             :                             " recoup_refresh_uuid"
    1315             :                             ",timestamp"
    1316             :                             ",old_coins.coin_pub AS old_coin_pub"
    1317             :                             ",old_denoms.denom_pub_hash AS old_denom_pub_hash"
    1318             :                             ",new_coins.coin_pub As coin_pub"
    1319             :                             ",coin_sig"
    1320             :                             ",coin_blind"
    1321             :                             ",new_denoms.denom_pub AS denom_pub"
    1322             :                             ",rrc.h_coin_ev AS h_blind_ev"
    1323             :                             ",new_denoms.denom_pub_hash"
    1324             :                             ",new_coins.denom_sig AS denom_sig"
    1325             :                             ",amount_val"
    1326             :                             ",amount_frac"
    1327             :                             " FROM recoup_refresh"
    1328             :                             "    INNER JOIN refresh_revealed_coins rrc"
    1329             :                             "      USING (rrc_serial)"
    1330             :                             "    INNER JOIN refresh_commitments rfc"
    1331             :                             "      ON (rrc.melt_serial_id = rfc.melt_serial_id)"
    1332             :                             "    INNER JOIN known_coins old_coins"
    1333             :                             "      ON (rfc.old_known_coin_id = old_coins.known_coin_id)"
    1334             :                             "    INNER JOIN known_coins new_coins"
    1335             :                             "      ON (new_coins.known_coin_id = recoup_refresh.known_coin_id)"
    1336             :                             "    INNER JOIN denominations new_denoms"
    1337             :                             "      ON (new_coins.denominations_serial = new_denoms.denominations_serial)"
    1338             :                             "    INNER JOIN denominations old_denoms"
    1339             :                             "      ON (old_coins.denominations_serial = old_denoms.denominations_serial)"
    1340             :                             " WHERE recoup_refresh_uuid>=$1"
    1341             :                             " ORDER BY recoup_refresh_uuid ASC;",
    1342             :                             1),
    1343             :     /* Used in #postgres_select_reserve_closed_above_serial_id() to
    1344             :        obtain information about closed reserves */
    1345         580 :     GNUNET_PQ_make_prepare ("reserves_close_get_incr",
    1346             :                             "SELECT"
    1347             :                             " close_uuid"
    1348             :                             ",reserves.reserve_pub"
    1349             :                             ",execution_date"
    1350             :                             ",wtid"
    1351             :                             ",receiver_account"
    1352             :                             ",amount_val"
    1353             :                             ",amount_frac"
    1354             :                             ",closing_fee_val"
    1355             :                             ",closing_fee_frac"
    1356             :                             " FROM reserves_close"
    1357             :                             " JOIN reserves"
    1358             :                             "   USING (reserve_uuid)"
    1359             :                             " WHERE close_uuid>=$1"
    1360             :                             " ORDER BY close_uuid ASC;",
    1361             :                             1),
    1362             :     /* Used in #postgres_get_reserve_history() to obtain recoup transactions
    1363             :        for a reserve */
    1364         580 :     GNUNET_PQ_make_prepare ("recoup_by_reserve",
    1365             :                             "SELECT"
    1366             :                             " coins.coin_pub"
    1367             :                             ",coin_sig"
    1368             :                             ",coin_blind"
    1369             :                             ",amount_val"
    1370             :                             ",amount_frac"
    1371             :                             ",timestamp"
    1372             :                             ",denoms.denom_pub_hash"
    1373             :                             ",coins.denom_sig"
    1374             :                             " FROM recoup"
    1375             :                             "    JOIN known_coins coins"
    1376             :                             "      USING (known_coin_id)"
    1377             :                             "    JOIN denominations denoms"
    1378             :                             "      USING (denominations_serial)"
    1379             :                             "    JOIN reserves_out ro"
    1380             :                             "      USING (reserve_out_serial_id)"
    1381             :                             " WHERE ro.reserve_uuid="
    1382             :                             "   (SELECT reserve_uuid"
    1383             :                             "     FROM reserves"
    1384             :                             "    WHERE reserve_pub=$1);",
    1385             :                             1),
    1386             :     /* Used in #postgres_get_coin_transactions() to obtain recoup transactions
    1387             :        affecting old coins of refreshed coins */
    1388         580 :     GNUNET_PQ_make_prepare ("recoup_by_old_coin",
    1389             :                             "SELECT"
    1390             :                             " coins.coin_pub"
    1391             :                             ",coin_sig"
    1392             :                             ",coin_blind"
    1393             :                             ",amount_val"
    1394             :                             ",amount_frac"
    1395             :                             ",timestamp"
    1396             :                             ",denoms.denom_pub_hash"
    1397             :                             ",coins.denom_sig"
    1398             :                             ",recoup_refresh_uuid"
    1399             :                             " FROM recoup_refresh"
    1400             :                             " JOIN known_coins coins"
    1401             :                             "   USING (known_coin_id)"
    1402             :                             " JOIN denominations denoms"
    1403             :                             "   USING (denominations_serial)"
    1404             :                             " WHERE rrc_serial IN"
    1405             :                             "   (SELECT rrc.rrc_serial"
    1406             :                             "    FROM refresh_commitments"
    1407             :                             "       JOIN refresh_revealed_coins rrc"
    1408             :                             "           USING (melt_serial_id)"
    1409             :                             "    WHERE old_known_coin_id="
    1410             :                             "       (SELECT known_coin_id"
    1411             :                             "          FROM known_coins"
    1412             :                             "         WHERE coin_pub=$1));",
    1413             :                             1),
    1414             :     /* Used in #postgres_get_reserve_history() */
    1415         580 :     GNUNET_PQ_make_prepare ("close_by_reserve",
    1416             :                             "SELECT"
    1417             :                             " amount_val"
    1418             :                             ",amount_frac"
    1419             :                             ",closing_fee_val"
    1420             :                             ",closing_fee_frac"
    1421             :                             ",execution_date"
    1422             :                             ",receiver_account"
    1423             :                             ",wtid"
    1424             :                             " FROM reserves_close"
    1425             :                             " WHERE reserve_uuid="
    1426             :                             "   (SELECT reserve_uuid"
    1427             :                             "     FROM reserves"
    1428             :                             "    WHERE reserve_pub=$1);",
    1429             :                             1),
    1430             :     /* Used in #postgres_get_expired_reserves() */
    1431         580 :     GNUNET_PQ_make_prepare ("get_expired_reserves",
    1432             :                             "SELECT"
    1433             :                             " expiration_date"
    1434             :                             ",account_details"
    1435             :                             ",reserve_pub"
    1436             :                             ",current_balance_val"
    1437             :                             ",current_balance_frac"
    1438             :                             " FROM reserves"
    1439             :                             " WHERE expiration_date<=$1"
    1440             :                             "   AND (current_balance_val != 0 "
    1441             :                             "        OR current_balance_frac != 0)"
    1442             :                             " ORDER BY expiration_date ASC"
    1443             :                             " LIMIT 1;",
    1444             :                             1),
    1445             :     /* Used in #postgres_get_coin_transactions() to obtain recoup transactions
    1446             :        for a coin */
    1447         580 :     GNUNET_PQ_make_prepare ("recoup_by_coin",
    1448             :                             "SELECT"
    1449             :                             " reserves.reserve_pub"
    1450             :                             ",denoms.denom_pub_hash"
    1451             :                             ",coin_sig"
    1452             :                             ",coin_blind"
    1453             :                             ",amount_val"
    1454             :                             ",amount_frac"
    1455             :                             ",timestamp"
    1456             :                             ",recoup_uuid"
    1457             :                             " FROM recoup"
    1458             :                             " JOIN reserves_out ro"
    1459             :                             "   USING (reserve_out_serial_id)"
    1460             :                             " JOIN reserves"
    1461             :                             "   USING (reserve_uuid)"
    1462             :                             " JOIN known_coins coins"
    1463             :                             "   USING (known_coin_id)"
    1464             :                             " JOIN denominations denoms"
    1465             :                             "   ON (denoms.denominations_serial = coins.denominations_serial)"
    1466             :                             " WHERE coins.coin_pub=$1;",
    1467             :                             1),
    1468             :     /* Used in #postgres_get_coin_transactions() to obtain recoup transactions
    1469             :        for a refreshed coin */
    1470         580 :     GNUNET_PQ_make_prepare ("recoup_by_refreshed_coin",
    1471             :                             "SELECT"
    1472             :                             " old_coins.coin_pub AS old_coin_pub"
    1473             :                             ",coin_sig"
    1474             :                             ",coin_blind"
    1475             :                             ",amount_val"
    1476             :                             ",amount_frac"
    1477             :                             ",timestamp"
    1478             :                             ",denoms.denom_pub_hash"
    1479             :                             ",coins.denom_sig"
    1480             :                             ",recoup_refresh_uuid"
    1481             :                             " FROM recoup_refresh"
    1482             :                             "    JOIN refresh_revealed_coins rrc"
    1483             :                             "      USING (rrc_serial)"
    1484             :                             "    JOIN refresh_commitments rfc"
    1485             :                             "      ON (rrc.melt_serial_id = rfc.melt_serial_id)"
    1486             :                             "    JOIN known_coins old_coins"
    1487             :                             "      ON (rfc.old_known_coin_id = old_coins.known_coin_id)"
    1488             :                             "    JOIN known_coins coins"
    1489             :                             "      ON (recoup_refresh.known_coin_id = coins.known_coin_id)"
    1490             :                             "    JOIN denominations denoms"
    1491             :                             "      ON (denoms.denominations_serial = coins.denominations_serial)"
    1492             :                             " WHERE coins.coin_pub=$1;",
    1493             :                             1),
    1494             :     /* Used in #postgres_get_reserve_by_h_blind() */
    1495         580 :     GNUNET_PQ_make_prepare ("reserve_by_h_blind",
    1496             :                             "SELECT"
    1497             :                             " reserves.reserve_pub"
    1498             :                             " FROM reserves_out"
    1499             :                             " JOIN reserves"
    1500             :                             "   USING (reserve_uuid)"
    1501             :                             " WHERE h_blind_ev=$1"
    1502             :                             " LIMIT 1;",
    1503             :                             1),
    1504             :     /* Used in #postgres_get_old_coin_by_h_blind() */
    1505         580 :     GNUNET_PQ_make_prepare ("old_coin_by_h_blind",
    1506             :                             "SELECT"
    1507             :                             " okc.coin_pub AS old_coin_pub"
    1508             :                             " FROM refresh_revealed_coins rrc"
    1509             :                             " JOIN refresh_commitments rcom USING (melt_serial_id)"
    1510             :                             " JOIN known_coins okc ON (rcom.old_known_coin_id = okc.known_coin_id)"
    1511             :                             " WHERE h_coin_ev=$1"
    1512             :                             " LIMIT 1;",
    1513             :                             1),
    1514             :     /* Used in #postgres_lookup_auditor_timestamp() */
    1515         580 :     GNUNET_PQ_make_prepare ("lookup_auditor_timestamp",
    1516             :                             "SELECT"
    1517             :                             " last_change"
    1518             :                             " FROM auditors"
    1519             :                             " WHERE auditor_pub=$1;",
    1520             :                             1),
    1521             :     /* Used in #postgres_lookup_auditor_status() */
    1522         580 :     GNUNET_PQ_make_prepare ("lookup_auditor_status",
    1523             :                             "SELECT"
    1524             :                             " auditor_url"
    1525             :                             ",is_active"
    1526             :                             " FROM auditors"
    1527             :                             " WHERE auditor_pub=$1;",
    1528             :                             1),
    1529             :     /* Used in #postgres_lookup_wire_timestamp() */
    1530         580 :     GNUNET_PQ_make_prepare ("lookup_wire_timestamp",
    1531             :                             "SELECT"
    1532             :                             " last_change"
    1533             :                             " FROM wire_accounts"
    1534             :                             " WHERE payto_uri=$1;",
    1535             :                             1),
    1536             :     /* used in #postgres_insert_auditor() */
    1537         580 :     GNUNET_PQ_make_prepare ("insert_auditor",
    1538             :                             "INSERT INTO auditors "
    1539             :                             "(auditor_pub"
    1540             :                             ",auditor_name"
    1541             :                             ",auditor_url"
    1542             :                             ",is_active"
    1543             :                             ",last_change"
    1544             :                             ") VALUES "
    1545             :                             "($1, $2, $3, true, $4);",
    1546             :                             4),
    1547             :     /* used in #postgres_update_auditor() */
    1548         580 :     GNUNET_PQ_make_prepare ("update_auditor",
    1549             :                             "UPDATE auditors"
    1550             :                             " SET"
    1551             :                             "  auditor_url=$2"
    1552             :                             " ,auditor_name=$3"
    1553             :                             " ,is_active=$4"
    1554             :                             " ,last_change=$5"
    1555             :                             " WHERE auditor_pub=$1",
    1556             :                             5),
    1557             :     /* used in #postgres_insert_wire() */
    1558         580 :     GNUNET_PQ_make_prepare ("insert_wire",
    1559             :                             "INSERT INTO wire_accounts "
    1560             :                             "(payto_uri"
    1561             :                             ",master_sig"
    1562             :                             ",is_active"
    1563             :                             ",last_change"
    1564             :                             ") VALUES "
    1565             :                             "($1, $2, true, $3);",
    1566             :                             3),
    1567             :     /* used in #postgres_update_wire() */
    1568         580 :     GNUNET_PQ_make_prepare ("update_wire",
    1569             :                             "UPDATE wire_accounts"
    1570             :                             " SET"
    1571             :                             "  is_active=$2"
    1572             :                             " ,last_change=$3"
    1573             :                             " WHERE payto_uri=$1",
    1574             :                             3),
    1575             :     /* used in #postgres_update_wire() */
    1576         580 :     GNUNET_PQ_make_prepare ("get_wire_accounts",
    1577             :                             "SELECT"
    1578             :                             " payto_uri"
    1579             :                             ",master_sig"
    1580             :                             " FROM wire_accounts"
    1581             :                             " WHERE is_active",
    1582             :                             0),
    1583             :     /* used in #postgres_update_wire() */
    1584         580 :     GNUNET_PQ_make_prepare ("get_wire_fees",
    1585             :                             "SELECT"
    1586             :                             " wire_fee_val"
    1587             :                             ",wire_fee_frac"
    1588             :                             ",closing_fee_val"
    1589             :                             ",closing_fee_frac"
    1590             :                             ",start_date"
    1591             :                             ",end_date"
    1592             :                             ",master_sig"
    1593             :                             " FROM wire_fee"
    1594             :                             " WHERE wire_method=$1",
    1595             :                             1),
    1596             :     /* used in #postgres_insert_signkey_revocation() */
    1597         580 :     GNUNET_PQ_make_prepare ("insert_signkey_revocation",
    1598             :                             "INSERT INTO signkey_revocations "
    1599             :                             "(esk_serial"
    1600             :                             ",master_sig"
    1601             :                             ") SELECT esk_serial, $2 "
    1602             :                             "    FROM exchange_sign_keys"
    1603             :                             "   WHERE exchange_pub=$1;",
    1604             :                             2),
    1605             :     /* used in #postgres_insert_signkey_revocation() */
    1606         580 :     GNUNET_PQ_make_prepare ("lookup_signkey_revocation",
    1607             :                             "SELECT "
    1608             :                             " master_sig"
    1609             :                             " FROM signkey_revocations"
    1610             :                             " WHERE esk_serial="
    1611             :                             "   (SELECT esk_serial"
    1612             :                             "      FROM exchange_sign_keys"
    1613             :                             "     WHERE exchange_pub=$1);",
    1614             :                             1),
    1615             :     /* used in #postgres_insert_signkey() */
    1616         580 :     GNUNET_PQ_make_prepare ("insert_signkey",
    1617             :                             "INSERT INTO exchange_sign_keys "
    1618             :                             "(exchange_pub"
    1619             :                             ",valid_from"
    1620             :                             ",expire_sign"
    1621             :                             ",expire_legal"
    1622             :                             ",master_sig"
    1623             :                             ") VALUES "
    1624             :                             "($1, $2, $3, $4, $5);",
    1625             :                             5),
    1626             :     /* used in #postgres_lookup_signing_key() */
    1627         580 :     GNUNET_PQ_make_prepare ("lookup_signing_key",
    1628             :                             "SELECT"
    1629             :                             " valid_from"
    1630             :                             ",expire_sign"
    1631             :                             ",expire_legal"
    1632             :                             " FROM exchange_sign_keys"
    1633             :                             " WHERE exchange_pub=$1",
    1634             :                             1),
    1635             :     /* used in #postgres_lookup_denomination_key() */
    1636         580 :     GNUNET_PQ_make_prepare ("lookup_denomination_key",
    1637             :                             "SELECT"
    1638             :                             " valid_from"
    1639             :                             ",expire_withdraw"
    1640             :                             ",expire_deposit"
    1641             :                             ",expire_legal"
    1642             :                             ",coin_val"
    1643             :                             ",coin_frac"
    1644             :                             ",fee_withdraw_val"
    1645             :                             ",fee_withdraw_frac"
    1646             :                             ",fee_deposit_val"
    1647             :                             ",fee_deposit_frac"
    1648             :                             ",fee_refresh_val"
    1649             :                             ",fee_refresh_frac"
    1650             :                             ",fee_refund_val"
    1651             :                             ",fee_refund_frac"
    1652             :                             " FROM denominations"
    1653             :                             " WHERE denom_pub_hash=$1;",
    1654             :                             1),
    1655             :     /* used in #postgres_insert_auditor_denom_sig() */
    1656         580 :     GNUNET_PQ_make_prepare ("insert_auditor_denom_sig",
    1657             :                             "WITH ax AS"
    1658             :                             " (SELECT auditor_uuid"
    1659             :                             "    FROM auditors"
    1660             :                             "   WHERE auditor_pub=$1)"
    1661             :                             "INSERT INTO auditor_denom_sigs "
    1662             :                             "(auditor_uuid"
    1663             :                             ",denominations_serial"
    1664             :                             ",auditor_sig"
    1665             :                             ") SELECT ax.auditor_uuid, denominations_serial, $3 "
    1666             :                             "    FROM denominations"
    1667             :                             "   CROSS JOIN ax"
    1668             :                             "   WHERE denom_pub_hash=$2;",
    1669             :                             3),
    1670             :     /* used in #postgres_select_auditor_denom_sig() */
    1671         580 :     GNUNET_PQ_make_prepare ("select_auditor_denom_sig",
    1672             :                             "SELECT"
    1673             :                             " auditor_sig"
    1674             :                             " FROM auditor_denom_sigs"
    1675             :                             " WHERE auditor_uuid="
    1676             :                             "  (SELECT auditor_uuid"
    1677             :                             "    FROM auditors"
    1678             :                             "    WHERE auditor_pub=$1)"
    1679             :                             " AND denominations_serial="
    1680             :                             "  (SELECT denominations_serial"
    1681             :                             "    FROM denominations"
    1682             :                             "    WHERE denom_pub_hash=$2);",
    1683             :                             2),
    1684             :     /* used in #postgres_lookup_wire_fee_by_time() */
    1685         580 :     GNUNET_PQ_make_prepare ("lookup_wire_fee_by_time",
    1686             :                             "SELECT"
    1687             :                             " wire_fee_val"
    1688             :                             ",wire_fee_frac"
    1689             :                             ",closing_fee_val"
    1690             :                             ",closing_fee_frac"
    1691             :                             " FROM wire_fee"
    1692             :                             " WHERE wire_method=$1"
    1693             :                             " AND end_date > $2"
    1694             :                             " AND start_date < $3;",
    1695             :                             1),
    1696             :     /* used in #postgres_commit */
    1697         580 :     GNUNET_PQ_make_prepare ("do_commit",
    1698             :                             "COMMIT",
    1699             :                             0),
    1700             :     /* used in #postgres_lookup_serial_by_table() */
    1701         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_denominations",
    1702             :                             "SELECT"
    1703             :                             " denominations_serial AS serial"
    1704             :                             " FROM denominations"
    1705             :                             " ORDER BY denominations_serial DESC"
    1706             :                             " LIMIT 1;",
    1707             :                             0),
    1708         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_denomination_revocations",
    1709             :                             "SELECT"
    1710             :                             " denom_revocations_serial_id AS serial"
    1711             :                             " FROM denomination_revocations"
    1712             :                             " ORDER BY denom_revocations_serial_id DESC"
    1713             :                             " LIMIT 1;",
    1714             :                             0),
    1715         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_reserves",
    1716             :                             "SELECT"
    1717             :                             " reserve_uuid AS serial"
    1718             :                             " FROM reserves"
    1719             :                             " ORDER BY reserve_uuid DESC"
    1720             :                             " LIMIT 1;",
    1721             :                             0),
    1722         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_reserves_in",
    1723             :                             "SELECT"
    1724             :                             " reserve_in_serial_id AS serial"
    1725             :                             " FROM reserves_in"
    1726             :                             " ORDER BY reserve_in_serial_id DESC"
    1727             :                             " LIMIT 1;",
    1728             :                             0),
    1729         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_reserves_close",
    1730             :                             "SELECT"
    1731             :                             " close_uuid AS serial"
    1732             :                             " FROM reserves_close"
    1733             :                             " ORDER BY close_uuid DESC"
    1734             :                             " LIMIT 1;",
    1735             :                             0),
    1736         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_reserves_out",
    1737             :                             "SELECT"
    1738             :                             " reserve_out_serial_id AS serial"
    1739             :                             " FROM reserves_out"
    1740             :                             " ORDER BY reserve_out_serial_id DESC"
    1741             :                             " LIMIT 1;",
    1742             :                             0),
    1743         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_auditors",
    1744             :                             "SELECT"
    1745             :                             " auditor_uuid AS serial"
    1746             :                             " FROM auditors"
    1747             :                             " ORDER BY auditor_uuid DESC"
    1748             :                             " LIMIT 1;",
    1749             :                             0),
    1750         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_auditor_denom_sigs",
    1751             :                             "SELECT"
    1752             :                             " auditor_denom_serial AS serial"
    1753             :                             " FROM auditor_denom_sigs"
    1754             :                             " ORDER BY auditor_denom_serial DESC"
    1755             :                             " LIMIT 1;",
    1756             :                             0),
    1757         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_exchange_sign_keys",
    1758             :                             "SELECT"
    1759             :                             " esk_serial AS serial"
    1760             :                             " FROM exchange_sign_keys"
    1761             :                             " ORDER BY esk_serial DESC"
    1762             :                             " LIMIT 1;",
    1763             :                             0),
    1764         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_signkey_revocations",
    1765             :                             "SELECT"
    1766             :                             " signkey_revocations_serial_id AS serial"
    1767             :                             " FROM signkey_revocations"
    1768             :                             " ORDER BY signkey_revocations_serial_id DESC"
    1769             :                             " LIMIT 1;",
    1770             :                             0),
    1771         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_known_coins",
    1772             :                             "SELECT"
    1773             :                             " known_coin_id AS serial"
    1774             :                             " FROM known_coins"
    1775             :                             " ORDER BY known_coin_id DESC"
    1776             :                             " LIMIT 1;",
    1777             :                             0),
    1778         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_refresh_commitments",
    1779             :                             "SELECT"
    1780             :                             " melt_serial_id AS serial"
    1781             :                             " FROM refresh_commitments"
    1782             :                             " ORDER BY melt_serial_id DESC"
    1783             :                             " LIMIT 1;",
    1784             :                             0),
    1785         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_refresh_revealed_coins",
    1786             :                             "SELECT"
    1787             :                             " rrc_serial AS serial"
    1788             :                             " FROM refresh_revealed_coins"
    1789             :                             " ORDER BY rrc_serial DESC"
    1790             :                             " LIMIT 1;",
    1791             :                             0),
    1792         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_refresh_transfer_keys",
    1793             :                             "SELECT"
    1794             :                             " rtc_serial AS serial"
    1795             :                             " FROM refresh_transfer_keys"
    1796             :                             " ORDER BY rtc_serial DESC"
    1797             :                             " LIMIT 1;",
    1798             :                             0),
    1799         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_deposits",
    1800             :                             "SELECT"
    1801             :                             " deposit_serial_id AS serial"
    1802             :                             " FROM deposits"
    1803             :                             " ORDER BY deposit_serial_id DESC"
    1804             :                             " LIMIT 1;",
    1805             :                             0),
    1806         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_refunds",
    1807             :                             "SELECT"
    1808             :                             " refund_serial_id AS serial"
    1809             :                             " FROM refunds"
    1810             :                             " ORDER BY refund_serial_id DESC"
    1811             :                             " LIMIT 1;",
    1812             :                             0),
    1813         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_wire_out",
    1814             :                             "SELECT"
    1815             :                             " wireout_uuid AS serial"
    1816             :                             " FROM wire_out"
    1817             :                             " ORDER BY wireout_uuid DESC"
    1818             :                             " LIMIT 1;",
    1819             :                             0),
    1820         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_aggregation_tracking",
    1821             :                             "SELECT"
    1822             :                             " aggregation_serial_id AS serial"
    1823             :                             " FROM aggregation_tracking"
    1824             :                             " ORDER BY aggregation_serial_id DESC"
    1825             :                             " LIMIT 1;",
    1826             :                             0),
    1827         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_wire_fee",
    1828             :                             "SELECT"
    1829             :                             " wire_fee_serial AS serial"
    1830             :                             " FROM wire_fee"
    1831             :                             " ORDER BY wire_fee_serial DESC"
    1832             :                             " LIMIT 1;",
    1833             :                             0),
    1834         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_recoup",
    1835             :                             "SELECT"
    1836             :                             " recoup_uuid AS serial"
    1837             :                             " FROM recoup"
    1838             :                             " ORDER BY recoup_uuid DESC"
    1839             :                             " LIMIT 1;",
    1840             :                             0),
    1841         580 :     GNUNET_PQ_make_prepare ("select_serial_by_table_recoup_refresh",
    1842             :                             "SELECT"
    1843             :                             " recoup_refresh_uuid AS serial"
    1844             :                             " FROM recoup_refresh"
    1845             :                             " ORDER BY recoup_refresh_uuid DESC"
    1846             :                             " LIMIT 1;",
    1847             :                             0),
    1848             :     /* For postgres_lookup_records_by_table */
    1849         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_denominations",
    1850             :                             "SELECT"
    1851             :                             " denominations_serial AS serial"
    1852             :                             ",denom_pub"
    1853             :                             ",master_sig"
    1854             :                             ",valid_from"
    1855             :                             ",expire_withdraw"
    1856             :                             ",expire_deposit"
    1857             :                             ",expire_legal"
    1858             :                             ",coin_val"
    1859             :                             ",coin_frac"
    1860             :                             ",fee_withdraw_val"
    1861             :                             ",fee_withdraw_frac"
    1862             :                             ",fee_deposit_val"
    1863             :                             ",fee_deposit_frac"
    1864             :                             ",fee_refresh_val"
    1865             :                             ",fee_refresh_frac"
    1866             :                             ",fee_refund_val"
    1867             :                             ",fee_refund_frac"
    1868             :                             " FROM denominations"
    1869             :                             " WHERE denominations_serial > $1"
    1870             :                             " ORDER BY denominations_serial ASC;",
    1871             :                             1),
    1872         580 :     GNUNET_PQ_make_prepare (
    1873             :       "select_above_serial_by_table_denomination_revocations",
    1874             :       "SELECT"
    1875             :       " denom_revocations_serial_id AS serial"
    1876             :       ",master_sig"
    1877             :       ",denominations_serial"
    1878             :       " FROM denomination_revocations"
    1879             :       " WHERE denom_revocations_serial_id > $1"
    1880             :       " ORDER BY denom_revocations_serial_id ASC;",
    1881             :       1),
    1882         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_reserves",
    1883             :                             "SELECT"
    1884             :                             " reserve_uuid AS serial"
    1885             :                             ",reserve_pub"
    1886             :                             ",account_details"
    1887             :                             ",current_balance_val"
    1888             :                             ",current_balance_frac"
    1889             :                             ",expiration_date"
    1890             :                             ",gc_date"
    1891             :                             " FROM reserves"
    1892             :                             " WHERE reserve_uuid > $1"
    1893             :                             " ORDER BY reserve_uuid ASC;",
    1894             :                             1),
    1895         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_reserves_in",
    1896             :                             "SELECT"
    1897             :                             " reserve_in_serial_id AS serial"
    1898             :                             ",wire_reference"
    1899             :                             ",credit_val"
    1900             :                             ",credit_frac"
    1901             :                             ",sender_account_details"
    1902             :                             ",exchange_account_section"
    1903             :                             ",execution_date"
    1904             :                             ",reserve_uuid"
    1905             :                             " FROM reserves_in"
    1906             :                             " WHERE reserve_in_serial_id > $1"
    1907             :                             " ORDER BY reserve_in_serial_id ASC;",
    1908             :                             1),
    1909         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_reserves_close",
    1910             :                             "SELECT"
    1911             :                             " close_uuid AS serial"
    1912             :                             ",execution_date"
    1913             :                             ",wtid"
    1914             :                             ",receiver_account"
    1915             :                             ",amount_val"
    1916             :                             ",amount_frac"
    1917             :                             ",closing_fee_val"
    1918             :                             ",closing_fee_frac"
    1919             :                             ",reserve_uuid"
    1920             :                             " FROM reserves_close"
    1921             :                             " WHERE close_uuid > $1"
    1922             :                             " ORDER BY close_uuid ASC;",
    1923             :                             1),
    1924         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_reserves_out",
    1925             :                             "SELECT"
    1926             :                             " reserve_out_serial_id AS serial"
    1927             :                             ",h_blind_ev"
    1928             :                             ",denom_sig"
    1929             :                             ",reserve_sig"
    1930             :                             ",execution_date"
    1931             :                             ",amount_with_fee_val"
    1932             :                             ",amount_with_fee_frac"
    1933             :                             ",reserve_uuid"
    1934             :                             ",denominations_serial"
    1935             :                             " FROM reserves_out"
    1936             :                             " WHERE reserve_out_serial_id > $1"
    1937             :                             " ORDER BY reserve_out_serial_id ASC;",
    1938             :                             1),
    1939         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_auditors",
    1940             :                             "SELECT"
    1941             :                             " auditor_uuid AS serial"
    1942             :                             ",auditor_pub"
    1943             :                             ",auditor_name"
    1944             :                             ",auditor_url"
    1945             :                             ",is_active"
    1946             :                             ",last_change"
    1947             :                             " FROM auditors"
    1948             :                             " WHERE auditor_uuid > $1"
    1949             :                             " ORDER BY auditor_uuid ASC;",
    1950             :                             1),
    1951         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_auditor_denom_sigs",
    1952             :                             "SELECT"
    1953             :                             " auditor_denom_serial AS serial"
    1954             :                             ",auditor_uuid"
    1955             :                             ",denominations_serial"
    1956             :                             ",auditor_sig"
    1957             :                             " FROM auditor_denom_sigs"
    1958             :                             " WHERE auditor_denom_serial > $1"
    1959             :                             " ORDER BY auditor_denom_serial ASC;",
    1960             :                             1),
    1961         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_exchange_sign_keys",
    1962             :                             "SELECT"
    1963             :                             " esk_serial AS serial"
    1964             :                             ",exchange_pub"
    1965             :                             ",master_sig"
    1966             :                             ",valid_from"
    1967             :                             ",expire_sign"
    1968             :                             ",expire_legal"
    1969             :                             " FROM exchange_sign_keys"
    1970             :                             " WHERE esk_serial > $1"
    1971             :                             " ORDER BY esk_serial ASC;",
    1972             :                             1),
    1973         580 :     GNUNET_PQ_make_prepare (
    1974             :       "select_above_serial_by_table_signkey_revocations",
    1975             :       "SELECT"
    1976             :       " signkey_revocations_serial_id AS serial"
    1977             :       ",esk_serial"
    1978             :       ",master_sig"
    1979             :       " FROM signkey_revocations"
    1980             :       " WHERE signkey_revocations_serial_id > $1"
    1981             :       " ORDER BY signkey_revocations_serial_id ASC;",
    1982             :       1),
    1983         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_known_coins",
    1984             :                             "SELECT"
    1985             :                             " known_coin_id AS serial"
    1986             :                             ",coin_pub"
    1987             :                             ",denom_sig"
    1988             :                             ",denominations_serial"
    1989             :                             " FROM known_coins"
    1990             :                             " WHERE known_coin_id > $1"
    1991             :                             " ORDER BY known_coin_id ASC;",
    1992             :                             1),
    1993         580 :     GNUNET_PQ_make_prepare (
    1994             :       "select_above_serial_by_table_refresh_commitments",
    1995             :       "SELECT"
    1996             :       " melt_serial_id AS serial"
    1997             :       ",rc"
    1998             :       ",old_known_coin_id"
    1999             :       ",old_coin_sig"
    2000             :       ",amount_with_fee_val"
    2001             :       ",amount_with_fee_frac"
    2002             :       ",noreveal_index"
    2003             :       " FROM refresh_commitments"
    2004             :       " WHERE melt_serial_id > $1"
    2005             :       " ORDER BY melt_serial_id ASC;",
    2006             :       1),
    2007         580 :     GNUNET_PQ_make_prepare (
    2008             :       "select_above_serial_by_table_refresh_revealed_coins",
    2009             :       "SELECT"
    2010             :       " rrc_serial AS serial"
    2011             :       ",freshcoin_index"
    2012             :       ",link_sig"
    2013             :       ",coin_ev"
    2014             :       ",h_coin_ev"
    2015             :       ",ev_sig"
    2016             :       ",melt_serial_id"
    2017             :       ",denominations_serial"
    2018             :       " FROM refresh_revealed_coins"
    2019             :       " WHERE rrc_serial > $1"
    2020             :       " ORDER BY rrc_serial ASC;",
    2021             :       1),
    2022         580 :     GNUNET_PQ_make_prepare (
    2023             :       "select_above_serial_by_table_refresh_transfer_keys",
    2024             :       "SELECT"
    2025             :       " rtc_serial AS serial"
    2026             :       ",transfer_pub"
    2027             :       ",transfer_privs"
    2028             :       ",melt_serial_id"
    2029             :       " FROM refresh_transfer_keys"
    2030             :       " WHERE rtc_serial > $1"
    2031             :       " ORDER BY rtc_serial ASC;",
    2032             :       1),
    2033         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_deposits",
    2034             :                             "SELECT"
    2035             :                             " deposit_serial_id AS serial"
    2036             :                             ",amount_with_fee_val"
    2037             :                             ",amount_with_fee_frac"
    2038             :                             ",wallet_timestamp"
    2039             :                             ",exchange_timestamp"
    2040             :                             ",refund_deadline"
    2041             :                             ",wire_deadline"
    2042             :                             ",merchant_pub"
    2043             :                             ",h_contract_terms"
    2044             :                             ",h_wire"
    2045             :                             ",coin_sig"
    2046             :                             ",wire"
    2047             :                             ",tiny"
    2048             :                             ",done"
    2049             :                             ",known_coin_id"
    2050             :                             " FROM deposits"
    2051             :                             " WHERE deposit_serial_id > $1"
    2052             :                             " ORDER BY deposit_serial_id ASC;",
    2053             :                             1),
    2054         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_refunds",
    2055             :                             "SELECT"
    2056             :                             " refund_serial_id AS serial"
    2057             :                             ",merchant_sig"
    2058             :                             ",rtransaction_id"
    2059             :                             ",amount_with_fee_val"
    2060             :                             ",amount_with_fee_frac"
    2061             :                             ",deposit_serial_id"
    2062             :                             " FROM refunds"
    2063             :                             " WHERE refund_serial_id > $1"
    2064             :                             " ORDER BY refund_serial_id ASC;",
    2065             :                             1),
    2066         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_wire_out",
    2067             :                             "SELECT"
    2068             :                             " wireout_uuid AS serial"
    2069             :                             ",execution_date"
    2070             :                             ",wtid_raw"
    2071             :                             ",wire_target"
    2072             :                             ",exchange_account_section"
    2073             :                             ",amount_val"
    2074             :                             ",amount_frac"
    2075             :                             " FROM wire_out"
    2076             :                             " WHERE wireout_uuid > $1"
    2077             :                             " ORDER BY wireout_uuid ASC;",
    2078             :                             1),
    2079         580 :     GNUNET_PQ_make_prepare (
    2080             :       "select_above_serial_by_table_aggregation_tracking",
    2081             :       "SELECT"
    2082             :       " aggregation_serial_id AS serial"
    2083             :       ",deposit_serial_id"
    2084             :       ",wtid_raw"
    2085             :       " FROM aggregation_tracking"
    2086             :       " WHERE aggregation_serial_id > $1"
    2087             :       " ORDER BY aggregation_serial_id ASC;",
    2088             :       1),
    2089         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_wire_fee",
    2090             :                             "SELECT"
    2091             :                             " wire_fee_serial AS serial"
    2092             :                             ",wire_method"
    2093             :                             ",start_date"
    2094             :                             ",end_date"
    2095             :                             ",wire_fee_val"
    2096             :                             ",wire_fee_frac"
    2097             :                             ",closing_fee_val"
    2098             :                             ",closing_fee_frac"
    2099             :                             ",master_sig"
    2100             :                             " FROM wire_fee"
    2101             :                             " WHERE wire_fee_serial > $1"
    2102             :                             " ORDER BY wire_fee_serial ASC;",
    2103             :                             1),
    2104         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_recoup",
    2105             :                             "SELECT"
    2106             :                             " recoup_uuid AS serial"
    2107             :                             ",coin_sig"
    2108             :                             ",coin_blind"
    2109             :                             ",amount_val"
    2110             :                             ",amount_frac"
    2111             :                             ",timestamp"
    2112             :                             ",known_coin_id"
    2113             :                             ",reserve_out_serial_id"
    2114             :                             " FROM recoup"
    2115             :                             " WHERE recoup_uuid > $1"
    2116             :                             " ORDER BY recoup_uuid ASC;",
    2117             :                             1),
    2118         580 :     GNUNET_PQ_make_prepare ("select_above_serial_by_table_recoup_refresh",
    2119             :                             "SELECT"
    2120             :                             " recoup_refresh_uuid AS serial"
    2121             :                             ",coin_sig"
    2122             :                             ",coin_blind"
    2123             :                             ",amount_val"
    2124             :                             ",amount_frac"
    2125             :                             ",timestamp"
    2126             :                             ",known_coin_id"
    2127             :                             ",rrc_serial"
    2128             :                             " FROM recoup_refresh"
    2129             :                             " WHERE recoup_refresh_uuid > $1"
    2130             :                             " ORDER BY recoup_refresh_uuid ASC;",
    2131             :                             1),
    2132             :     /* For postgres_insert_records_by_table */
    2133         580 :     GNUNET_PQ_make_prepare ("insert_into_table_denominations",
    2134             :                             "INSERT INTO denominations"
    2135             :                             "(denominations_serial"
    2136             :                             ",denom_pub_hash"
    2137             :                             ",denom_pub"
    2138             :                             ",master_sig"
    2139             :                             ",valid_from"
    2140             :                             ",expire_withdraw"
    2141             :                             ",expire_deposit"
    2142             :                             ",expire_legal"
    2143             :                             ",coin_val"
    2144             :                             ",coin_frac"
    2145             :                             ",fee_withdraw_val"
    2146             :                             ",fee_withdraw_frac"
    2147             :                             ",fee_deposit_val"
    2148             :                             ",fee_deposit_frac"
    2149             :                             ",fee_refresh_val"
    2150             :                             ",fee_refresh_frac"
    2151             :                             ",fee_refund_val"
    2152             :                             ",fee_refund_frac"
    2153             :                             ") VALUES "
    2154             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
    2155             :                             " $11, $12, $13, $14, $15, $16, $17, $18);",
    2156             :                             18),
    2157         580 :     GNUNET_PQ_make_prepare ("insert_into_table_denomination_revocations",
    2158             :                             "INSERT INTO denomination_revocations"
    2159             :                             "(denom_revocations_serial_id"
    2160             :                             ",master_sig"
    2161             :                             ",denominations_serial"
    2162             :                             ") VALUES "
    2163             :                             "($1, $2, $3);",
    2164             :                             3),
    2165         580 :     GNUNET_PQ_make_prepare ("insert_into_table_reserves",
    2166             :                             "INSERT INTO reserves"
    2167             :                             "(reserve_uuid"
    2168             :                             ",reserve_pub"
    2169             :                             ",account_details"
    2170             :                             ",current_balance_val"
    2171             :                             ",current_balance_frac"
    2172             :                             ",expiration_date"
    2173             :                             ",gc_date"
    2174             :                             ") VALUES "
    2175             :                             "($1, $2, $3, $4, $5, $6, $7);",
    2176             :                             7),
    2177         580 :     GNUNET_PQ_make_prepare ("insert_into_table_reserves_in",
    2178             :                             "INSERT INTO reserves_in"
    2179             :                             "(reserve_in_serial_id"
    2180             :                             ",wire_reference"
    2181             :                             ",credit_val"
    2182             :                             ",credit_frac"
    2183             :                             ",sender_account_details"
    2184             :                             ",exchange_account_section"
    2185             :                             ",execution_date"
    2186             :                             ",reserve_uuid"
    2187             :                             ") VALUES "
    2188             :                             "($1, $2, $3, $4, $5, $6, $7, $8);",
    2189             :                             8),
    2190         580 :     GNUNET_PQ_make_prepare ("insert_into_table_reserves_close",
    2191             :                             "INSERT INTO reserves_close"
    2192             :                             "(close_uuid"
    2193             :                             ",execution_date"
    2194             :                             ",wtid"
    2195             :                             ",receiver_account"
    2196             :                             ",amount_val"
    2197             :                             ",amount_frac"
    2198             :                             ",closing_fee_val"
    2199             :                             ",closing_fee_frac"
    2200             :                             ",reserve_uuid"
    2201             :                             ") VALUES "
    2202             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
    2203             :                             9),
    2204         580 :     GNUNET_PQ_make_prepare ("insert_into_table_reserves_out",
    2205             :                             "INSERT INTO reserves_out"
    2206             :                             "(reserve_out_serial_id"
    2207             :                             ",h_blind_ev"
    2208             :                             ",denom_sig"
    2209             :                             ",reserve_sig"
    2210             :                             ",execution_date"
    2211             :                             ",amount_with_fee_val"
    2212             :                             ",amount_with_fee_frac"
    2213             :                             ",reserve_uuid"
    2214             :                             ",denominations_serial"
    2215             :                             ") VALUES "
    2216             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
    2217             :                             9),
    2218         580 :     GNUNET_PQ_make_prepare ("insert_into_table_auditors",
    2219             :                             "INSERT INTO auditors"
    2220             :                             "(auditor_uuid"
    2221             :                             ",auditor_pub"
    2222             :                             ",auditor_name"
    2223             :                             ",auditor_url"
    2224             :                             ",is_active"
    2225             :                             ",last_change"
    2226             :                             ") VALUES "
    2227             :                             "($1, $2, $3, $4, $5, $6);",
    2228             :                             6),
    2229         580 :     GNUNET_PQ_make_prepare ("insert_into_table_auditor_denom_sigs",
    2230             :                             "INSERT INTO auditor_denom_sigs"
    2231             :                             "(auditor_denom_serial"
    2232             :                             ",auditor_uuid"
    2233             :                             ",denominations_serial"
    2234             :                             ",auditor_sig"
    2235             :                             ") VALUES "
    2236             :                             "($1, $2, $3, $4);",
    2237             :                             4),
    2238         580 :     GNUNET_PQ_make_prepare ("insert_into_table_exchange_sign_keys",
    2239             :                             "INSERT INTO exchange_sign_keys"
    2240             :                             "(esk_serial"
    2241             :                             ",exchange_pub"
    2242             :                             ",master_sig"
    2243             :                             ",valid_from"
    2244             :                             ",expire_sign"
    2245             :                             ",expire_legal"
    2246             :                             ") VALUES "
    2247             :                             "($1, $2, $3, $4, $5, $6);",
    2248             :                             6),
    2249         580 :     GNUNET_PQ_make_prepare ("insert_into_table_signkey_revocations",
    2250             :                             "INSERT INTO signkey_revocations"
    2251             :                             "(signkey_revocations_serial_id"
    2252             :                             ",esk_serial"
    2253             :                             ",master_sig"
    2254             :                             ") VALUES "
    2255             :                             "($1, $2, $3);",
    2256             :                             3),
    2257         580 :     GNUNET_PQ_make_prepare ("insert_into_table_known_coins",
    2258             :                             "INSERT INTO known_coins"
    2259             :                             "(known_coin_id"
    2260             :                             ",coin_pub"
    2261             :                             ",denom_sig"
    2262             :                             ",denominations_serial"
    2263             :                             ") VALUES "
    2264             :                             "($1, $2, $3, $4);",
    2265             :                             4),
    2266         580 :     GNUNET_PQ_make_prepare ("insert_into_table_refresh_commitments",
    2267             :                             "INSERT INTO refresh_commitments"
    2268             :                             "(melt_serial_id"
    2269             :                             ",rc"
    2270             :                             ",old_coin_sig"
    2271             :                             ",amount_with_fee_val"
    2272             :                             ",amount_with_fee_frac"
    2273             :                             ",noreveal_index"
    2274             :                             ",old_known_coin_id"
    2275             :                             ") VALUES "
    2276             :                             "($1, $2, $3, $4, $5, $6, $7);",
    2277             :                             7),
    2278         580 :     GNUNET_PQ_make_prepare ("insert_into_table_refresh_revealed_coins",
    2279             :                             "INSERT INTO refresh_revealed_coins"
    2280             :                             "(rrc_serial"
    2281             :                             ",freshcoin_index"
    2282             :                             ",link_sig"
    2283             :                             ",coin_ev"
    2284             :                             ",h_coin_ev"
    2285             :                             ",ev_sig"
    2286             :                             ",denominations_serial"
    2287             :                             ",melt_serial_id"
    2288             :                             ") VALUES "
    2289             :                             "($1, $2, $3, $4, $5, $6, $7, $8);",
    2290             :                             8),
    2291         580 :     GNUNET_PQ_make_prepare ("insert_into_table_refresh_transfer_keys",
    2292             :                             "INSERT INTO refresh_transfer_keys"
    2293             :                             "(rtc_serial"
    2294             :                             ",transfer_pub"
    2295             :                             ",transfer_privs"
    2296             :                             ",melt_serial_id"
    2297             :                             ") VALUES "
    2298             :                             "($1, $2, $3, $4);",
    2299             :                             4),
    2300         580 :     GNUNET_PQ_make_prepare ("insert_into_table_deposits",
    2301             :                             "INSERT INTO deposits"
    2302             :                             "(deposit_serial_id"
    2303             :                             ",amount_with_fee_val"
    2304             :                             ",amount_with_fee_frac"
    2305             :                             ",wallet_timestamp"
    2306             :                             ",exchange_timestamp"
    2307             :                             ",refund_deadline"
    2308             :                             ",wire_deadline"
    2309             :                             ",merchant_pub"
    2310             :                             ",h_contract_terms"
    2311             :                             ",h_wire"
    2312             :                             ",coin_sig"
    2313             :                             ",wire"
    2314             :                             ",tiny"
    2315             :                             ",done"
    2316             :                             ",known_coin_id"
    2317             :                             ") VALUES "
    2318             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
    2319             :                             " $11, $12, $13, $14, $15);",
    2320             :                             15),
    2321         580 :     GNUNET_PQ_make_prepare ("insert_into_table_refunds",
    2322             :                             "INSERT INTO refunds"
    2323             :                             "(refund_serial_id"
    2324             :                             ",merchant_sig"
    2325             :                             ",rtransaction_id"
    2326             :                             ",amount_with_fee_val"
    2327             :                             ",amount_with_fee_frac"
    2328             :                             ",deposit_serial_id"
    2329             :                             ") VALUES "
    2330             :                             "($1, $2, $3, $4, $5, $6);",
    2331             :                             6),
    2332         580 :     GNUNET_PQ_make_prepare ("insert_into_table_wire_out",
    2333             :                             "INSERT INTO wire_out"
    2334             :                             "(wireout_uuid"
    2335             :                             ",execution_date"
    2336             :                             ",wtid_raw"
    2337             :                             ",wire_target"
    2338             :                             ",exchange_account_section"
    2339             :                             ",amount_val"
    2340             :                             ",amount_frac"
    2341             :                             ") VALUES "
    2342             :                             "($1, $2, $3, $4, $5, $6, $7);",
    2343             :                             7),
    2344         580 :     GNUNET_PQ_make_prepare ("insert_into_table_aggregation_tracking",
    2345             :                             "INSERT INTO aggregation_tracking"
    2346             :                             "(aggregation_serial_id"
    2347             :                             ",deposit_serial_id"
    2348             :                             ",wtid_raw"
    2349             :                             ") VALUES "
    2350             :                             "($1, $2, $3);",
    2351             :                             3),
    2352         580 :     GNUNET_PQ_make_prepare ("insert_into_table_wire_fee",
    2353             :                             "INSERT INTO wire_fee"
    2354             :                             "(wire_fee_serial"
    2355             :                             ",wire_method"
    2356             :                             ",start_date"
    2357             :                             ",end_date"
    2358             :                             ",wire_fee_val"
    2359             :                             ",wire_fee_frac"
    2360             :                             ",closing_fee_val"
    2361             :                             ",closing_fee_frac"
    2362             :                             ",master_sig"
    2363             :                             ") VALUES "
    2364             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
    2365             :                             9),
    2366         580 :     GNUNET_PQ_make_prepare ("insert_into_table_recoup",
    2367             :                             "INSERT INTO recoup"
    2368             :                             "(recoup_uuid"
    2369             :                             ",coin_sig"
    2370             :                             ",coin_blind"
    2371             :                             ",amount_val"
    2372             :                             ",amount_frac"
    2373             :                             ",timestamp"
    2374             :                             ",known_coin_id"
    2375             :                             ",reserve_out_serial_id"
    2376             :                             ") VALUES "
    2377             :                             "($1, $2, $3, $4, $5, $6, $7, $8);",
    2378             :                             8),
    2379         580 :     GNUNET_PQ_make_prepare ("insert_into_table_recoup_refresh",
    2380             :                             "INSERT INTO recoup_refresh"
    2381             :                             "(recoup_refresh_uuid"
    2382             :                             ",coin_sig"
    2383             :                             ",coin_blind"
    2384             :                             ",amount_val"
    2385             :                             ",amount_frac"
    2386             :                             ",timestamp"
    2387             :                             ",known_coin_id"
    2388             :                             ",rrc_serial"
    2389             :                             ") VALUES "
    2390             :                             "($1, $2, $3, $4, $5, $6, $7, $8);",
    2391             :                             8),
    2392             : 
    2393             :     /* Used in #postgres_begin_shard() */
    2394         580 :     GNUNET_PQ_make_prepare ("get_open_shard",
    2395             :                             "SELECT"
    2396             :                             " start_row"
    2397             :                             ",end_row"
    2398             :                             " FROM work_shards"
    2399             :                             " WHERE job_name=$1"
    2400             :                             "   AND last_attempt<$2"
    2401             :                             "   AND completed=FALSE"
    2402             :                             " ORDER BY last_attempt ASC"
    2403             :                             " LIMIT 1;",
    2404             :                             2),
    2405         580 :     GNUNET_PQ_make_prepare ("reclaim_shard",
    2406             :                             "UPDATE work_shards"
    2407             :                             " SET last_attempt=$2"
    2408             :                             " WHERE job_name=$1"
    2409             :                             "   AND start_row=$3"
    2410             :                             "   AND end_row=$4",
    2411             :                             4),
    2412         580 :     GNUNET_PQ_make_prepare ("get_last_shard",
    2413             :                             "SELECT"
    2414             :                             " end_row"
    2415             :                             " FROM work_shards"
    2416             :                             " WHERE job_name=$1"
    2417             :                             " ORDER BY end_row DESC"
    2418             :                             " LIMIT 1;",
    2419             :                             1),
    2420         580 :     GNUNET_PQ_make_prepare ("claim_next_shard",
    2421             :                             "INSERT INTO work_shards"
    2422             :                             "(job_name"
    2423             :                             ",last_attempt"
    2424             :                             ",start_row"
    2425             :                             ",end_row"
    2426             :                             ") VALUES "
    2427             :                             "($1, $2, $3, $4);",
    2428             :                             4),
    2429             :     /* Used in #postgres_complete_shard() */
    2430         580 :     GNUNET_PQ_make_prepare ("complete_shard",
    2431             :                             "UPDATE work_shards"
    2432             :                             " SET completed=TRUE"
    2433             :                             " WHERE job_name=$1"
    2434             :                             "   AND start_row=$2"
    2435             :                             "   AND end_row=$3",
    2436             :                             3),
    2437             :     GNUNET_PQ_PREPARED_STATEMENT_END
    2438             :   };
    2439             : 
    2440         580 :   ret = GNUNET_PQ_prepare_statements (pg->conn,
    2441             :                                       ps);
    2442         580 :   if (GNUNET_OK != ret)
    2443           0 :     return ret;
    2444         580 :   pg->init = true;
    2445         580 :   return GNUNET_OK;
    2446             : }
    2447             : 
    2448             : 
    2449             : /**
    2450             :  * Connect to the database if the connection does not exist yet.
    2451             :  *
    2452             :  * @param pg the plugin-specific state
    2453             :  * @param skip_prepare true if we should skip prepared statement setup
    2454             :  * @return #GNUNET_OK on success
    2455             :  */
    2456             : static enum GNUNET_GenericReturnValue
    2457        1207 : internal_setup (struct PostgresClosure *pg,
    2458             :                 bool skip_prepare)
    2459             : {
    2460        1207 :   if (NULL == pg->conn)
    2461             :   {
    2462             : #if AUTO_EXPLAIN
    2463             :     /* Enable verbose logging to see where queries do not
    2464             :        properly use indices */
    2465         627 :     struct GNUNET_PQ_ExecuteStatement es[] = {
    2466         627 :       GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"),
    2467         627 :       GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"),
    2468         627 :       GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"),
    2469         627 :       GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"),
    2470             :       /* https://wiki.postgresql.org/wiki/Serializable suggests to really
    2471             :          force the default to 'serializable' if SSI is to be used. */
    2472         627 :       GNUNET_PQ_make_try_execute (
    2473             :         "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
    2474         627 :       GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
    2475         627 :       GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
    2476             :       GNUNET_PQ_EXECUTE_STATEMENT_END
    2477             :     };
    2478             : #else
    2479             :     struct GNUNET_PQ_ExecuteStatement *es = NULL;
    2480             : #endif
    2481             :     struct GNUNET_PQ_Context *db_conn;
    2482             : 
    2483         627 :     db_conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
    2484             :                                           "exchangedb-postgres",
    2485             :                                           NULL,
    2486             :                                           es,
    2487             :                                           NULL);
    2488         627 :     if (NULL == db_conn)
    2489           0 :       return GNUNET_SYSERR;
    2490         627 :     pg->conn = db_conn;
    2491             :   }
    2492        1207 :   if (NULL == pg->transaction_name)
    2493        1207 :     GNUNET_PQ_reconnect_if_down (pg->conn);
    2494        1207 :   if (pg->init)
    2495           0 :     return GNUNET_OK;
    2496        1207 :   if (skip_prepare)
    2497         627 :     return GNUNET_OK;
    2498         580 :   return prepare_statements (pg);
    2499             : }
    2500             : 
    2501             : 
    2502             : /**
    2503             :  * Do a pre-flight check that we are not in an uncommitted transaction.
    2504             :  * If we are, try to commit the previous transaction and output a warning.
    2505             :  * Does not return anything, as we will continue regardless of the outcome.
    2506             :  *
    2507             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    2508             :  * @return #GNUNET_OK if everything is fine
    2509             :  *         #GNUNET_NO if a transaction was rolled back
    2510             :  *         #GNUNET_SYSERR on hard errors
    2511             :  */
    2512             : static enum GNUNET_GenericReturnValue
    2513        2981 : postgres_preflight (void *cls)
    2514             : {
    2515        2981 :   struct PostgresClosure *pg = cls;
    2516        2981 :   struct GNUNET_PQ_ExecuteStatement es[] = {
    2517        2981 :     GNUNET_PQ_make_execute ("ROLLBACK"),
    2518             :     GNUNET_PQ_EXECUTE_STATEMENT_END
    2519             :   };
    2520             : 
    2521        2981 :   if (! pg->init)
    2522             :   {
    2523         580 :     if (GNUNET_OK !=
    2524         580 :         internal_setup (pg,
    2525             :                         false))
    2526           0 :       return GNUNET_SYSERR;
    2527             :   }
    2528        2981 :   if (NULL == pg->transaction_name)
    2529        2980 :     return GNUNET_OK; /* all good */
    2530           1 :   if (GNUNET_OK ==
    2531           1 :       GNUNET_PQ_exec_statements (pg->conn,
    2532             :                                  es))
    2533             :   {
    2534           1 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2535             :                 "BUG: Preflight check rolled back transaction `%s'!\n",
    2536             :                 pg->transaction_name);
    2537             :   }
    2538             :   else
    2539             :   {
    2540           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2541             :                 "BUG: Preflight check failed to rollback transaction `%s'!\n",
    2542             :                 pg->transaction_name);
    2543             :   }
    2544           1 :   pg->transaction_name = NULL;
    2545           1 :   return GNUNET_NO;
    2546             : }
    2547             : 
    2548             : 
    2549             : /**
    2550             :  * Start a transaction.
    2551             :  *
    2552             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    2553             :  * @param name unique name identifying the transaction (for debugging)
    2554             :  *             must point to a constant
    2555             :  * @return #GNUNET_OK on success
    2556             :  */
    2557             : static int
    2558        1068 : postgres_start (void *cls,
    2559             :                 const char *name)
    2560             : {
    2561        1068 :   struct PostgresClosure *pg = cls;
    2562        1068 :   struct GNUNET_PQ_ExecuteStatement es[] = {
    2563        1068 :     GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"),
    2564             :     GNUNET_PQ_EXECUTE_STATEMENT_END
    2565             :   };
    2566             : 
    2567        1068 :   if (GNUNET_SYSERR ==
    2568        1068 :       postgres_preflight (pg))
    2569           0 :     return GNUNET_SYSERR;
    2570        1068 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2571             :               "Starting transaction named %s on %p\n",
    2572             :               name,
    2573             :               pg->conn);
    2574        1068 :   if (GNUNET_OK !=
    2575        1068 :       GNUNET_PQ_exec_statements (pg->conn,
    2576             :                                  es))
    2577             :   {
    2578           0 :     TALER_LOG_ERROR ("Failed to start transaction\n");
    2579           0 :     GNUNET_break (0);
    2580           0 :     return GNUNET_SYSERR;
    2581             :   }
    2582        1068 :   pg->transaction_name = name;
    2583        1068 :   return GNUNET_OK;
    2584             : }
    2585             : 
    2586             : 
    2587             : /**
    2588             :  * Start a READ COMMITTED transaction.
    2589             :  *
    2590             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    2591             :  * @param name unique name identifying the transaction (for debugging)
    2592             :  *             must point to a constant
    2593             :  * @return #GNUNET_OK on success
    2594             :  */
    2595             : static int
    2596          96 : postgres_start_read_committed (void *cls,
    2597             :                                const char *name)
    2598             : {
    2599          96 :   struct PostgresClosure *pg = cls;
    2600          96 :   struct GNUNET_PQ_ExecuteStatement es[] = {
    2601          96 :     GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL READ COMMITTED"),
    2602             :     GNUNET_PQ_EXECUTE_STATEMENT_END
    2603             :   };
    2604             : 
    2605          96 :   if (GNUNET_SYSERR ==
    2606          96 :       postgres_preflight (pg))
    2607           0 :     return GNUNET_SYSERR;
    2608          96 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2609             :               "Starting transaction named %s on %p\n",
    2610             :               name,
    2611             :               pg->conn);
    2612          96 :   if (GNUNET_OK !=
    2613          96 :       GNUNET_PQ_exec_statements (pg->conn,
    2614             :                                  es))
    2615             :   {
    2616           0 :     TALER_LOG_ERROR ("Failed to start transaction\n");
    2617           0 :     GNUNET_break (0);
    2618           0 :     return GNUNET_SYSERR;
    2619             :   }
    2620          96 :   pg->transaction_name = name;
    2621          96 :   return GNUNET_OK;
    2622             : }
    2623             : 
    2624             : 
    2625             : /**
    2626             :  * Roll back the current transaction of a database connection.
    2627             :  *
    2628             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    2629             :  */
    2630             : static void
    2631         239 : postgres_rollback (void *cls)
    2632             : {
    2633         239 :   struct PostgresClosure *pg = cls;
    2634         239 :   struct GNUNET_PQ_ExecuteStatement es[] = {
    2635         239 :     GNUNET_PQ_make_execute ("ROLLBACK"),
    2636             :     GNUNET_PQ_EXECUTE_STATEMENT_END
    2637             :   };
    2638             : 
    2639         239 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2640             :               "Rolling back transaction on %p\n",
    2641             :               pg->conn);
    2642         239 :   GNUNET_break (GNUNET_OK ==
    2643             :                 GNUNET_PQ_exec_statements (pg->conn,
    2644             :                                            es));
    2645         239 :   pg->transaction_name = NULL;
    2646         239 : }
    2647             : 
    2648             : 
    2649             : /**
    2650             :  * Commit the current transaction of a database connection.
    2651             :  *
    2652             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    2653             :  * @return final transaction status
    2654             :  */
    2655             : static enum GNUNET_DB_QueryStatus
    2656         954 : postgres_commit (void *cls)
    2657             : {
    2658         954 :   struct PostgresClosure *pg = cls;
    2659         954 :   struct GNUNET_PQ_QueryParam params[] = {
    2660             :     GNUNET_PQ_query_param_end
    2661             :   };
    2662             :   enum GNUNET_DB_QueryStatus qs;
    2663             : 
    2664         954 :   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    2665             :                                            "do_commit",
    2666             :                                            params);
    2667         954 :   pg->transaction_name = NULL;
    2668         954 :   return qs;
    2669             : }
    2670             : 
    2671             : 
    2672             : /**
    2673             :  * Register callback to be invoked on events of type @a es.
    2674             :  *
    2675             :  * @param cls database context to use
    2676             :  * @param timeout how long until to generate a timeout event
    2677             :  * @param es specification of the event to listen for
    2678             :  * @param cb function to call when the event happens, possibly
    2679             :  *         multiple times (until cancel is invoked)
    2680             :  * @param cb_cls closure for @a cb
    2681             :  * @return handle useful to cancel the listener
    2682             :  */
    2683             : static struct GNUNET_DB_EventHandler *
    2684          24 : postgres_event_listen (void *cls,
    2685             :                        struct GNUNET_TIME_Relative timeout,
    2686             :                        const struct GNUNET_DB_EventHeaderP *es,
    2687             :                        GNUNET_DB_EventCallback cb,
    2688             :                        void *cb_cls)
    2689             : {
    2690          24 :   struct PostgresClosure *pg = cls;
    2691             : 
    2692          24 :   return GNUNET_PQ_event_listen (pg->conn,
    2693             :                                  es,
    2694             :                                  timeout,
    2695             :                                  cb,
    2696             :                                  cb_cls);
    2697             : }
    2698             : 
    2699             : 
    2700             : /**
    2701             :  * Stop notifications.
    2702             :  *
    2703             :  * @param cls the plugin's `struct PostgresClosure`
    2704             :  * @param eh handle to unregister.
    2705             :  */
    2706             : static void
    2707          24 : postgres_event_listen_cancel (void *cls,
    2708             :                               struct GNUNET_DB_EventHandler *eh)
    2709             : {
    2710             :   (void) cls;
    2711          24 :   GNUNET_PQ_event_listen_cancel (eh);
    2712          24 : }
    2713             : 
    2714             : 
    2715             : /**
    2716             :  * Notify all that listen on @a es of an event.
    2717             :  *
    2718             :  * @param cls database context to use
    2719             :  * @param es specification of the event to generate
    2720             :  * @param extra additional event data provided
    2721             :  * @param extra_size number of bytes in @a extra
    2722             :  */
    2723             : static void
    2724          73 : postgres_event_notify (void *cls,
    2725             :                        const struct GNUNET_DB_EventHeaderP *es,
    2726             :                        const void *extra,
    2727             :                        size_t extra_size)
    2728             : {
    2729          73 :   struct PostgresClosure *pg = cls;
    2730             : 
    2731          73 :   GNUNET_PQ_event_notify (pg->conn,
    2732             :                           es,
    2733             :                           extra,
    2734             :                           extra_size);
    2735          73 : }
    2736             : 
    2737             : 
    2738             : /**
    2739             :  * Insert a denomination key's public information into the database for
    2740             :  * reference by auditors and other consistency checks.
    2741             :  *
    2742             :  * @param cls the @e cls of this struct with the plugin-specific state
    2743             :  * @param denom_pub the public key used for signing coins of this denomination
    2744             :  * @param issue issuing information with value, fees and other info about the coin
    2745             :  * @return status of the query
    2746             :  */
    2747             : static enum GNUNET_DB_QueryStatus
    2748          32 : postgres_insert_denomination_info (
    2749             :   void *cls,
    2750             :   const struct TALER_DenominationPublicKey *denom_pub,
    2751             :   const struct TALER_EXCHANGEDB_DenominationKeyInformationP *issue)
    2752             : {
    2753          32 :   struct PostgresClosure *pg = cls;
    2754          32 :   struct GNUNET_PQ_QueryParam params[] = {
    2755          32 :     GNUNET_PQ_query_param_auto_from_type (&issue->properties.denom_hash),
    2756          32 :     GNUNET_PQ_query_param_rsa_public_key (denom_pub->rsa_public_key),
    2757          32 :     GNUNET_PQ_query_param_auto_from_type (&issue->signature),
    2758          32 :     TALER_PQ_query_param_absolute_time_nbo (&issue->properties.start),
    2759          32 :     TALER_PQ_query_param_absolute_time_nbo (&issue->properties.expire_withdraw),
    2760          32 :     TALER_PQ_query_param_absolute_time_nbo (&issue->properties.expire_deposit),
    2761          32 :     TALER_PQ_query_param_absolute_time_nbo (&issue->properties.expire_legal),
    2762          32 :     TALER_PQ_query_param_amount_nbo (&issue->properties.value),
    2763          32 :     TALER_PQ_query_param_amount_nbo (&issue->properties.fee_withdraw),
    2764          32 :     TALER_PQ_query_param_amount_nbo (&issue->properties.fee_deposit),
    2765          32 :     TALER_PQ_query_param_amount_nbo (&issue->properties.fee_refresh),
    2766          32 :     TALER_PQ_query_param_amount_nbo (&issue->properties.fee_refund),
    2767             :     GNUNET_PQ_query_param_end
    2768             :   };
    2769             : 
    2770          32 :   GNUNET_assert (0 != GNUNET_TIME_absolute_ntoh (
    2771             :                    issue->properties.start).abs_value_us);
    2772          32 :   GNUNET_assert (0 != GNUNET_TIME_absolute_ntoh (
    2773             :                    issue->properties.expire_withdraw).abs_value_us);
    2774          32 :   GNUNET_assert (0 != GNUNET_TIME_absolute_ntoh (
    2775             :                    issue->properties.expire_deposit).abs_value_us);
    2776          32 :   GNUNET_assert (0 != GNUNET_TIME_absolute_ntoh (
    2777             :                    issue->properties.expire_legal).abs_value_us);
    2778             :   /* check fees match coin currency */
    2779          32 :   GNUNET_assert (GNUNET_YES ==
    2780             :                  TALER_amount_cmp_currency_nbo (&issue->properties.value,
    2781             :                                                 &issue->properties.fee_withdraw));
    2782          32 :   GNUNET_assert (GNUNET_YES ==
    2783             :                  TALER_amount_cmp_currency_nbo (&issue->properties.value,
    2784             :                                                 &issue->properties.fee_deposit));
    2785          32 :   GNUNET_assert (GNUNET_YES ==
    2786             :                  TALER_amount_cmp_currency_nbo (&issue->properties.value,
    2787             :                                                 &issue->properties.fee_refresh));
    2788          32 :   GNUNET_assert (GNUNET_YES ==
    2789             :                  TALER_amount_cmp_currency_nbo (&issue->properties.value,
    2790             :                                                 &issue->properties.fee_refund));
    2791             : 
    2792          32 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    2793             :                                              "denomination_insert",
    2794             :                                              params);
    2795             : }
    2796             : 
    2797             : 
    2798             : /**
    2799             :  * Fetch information about a denomination key.
    2800             :  *
    2801             :  * @param cls the @e cls of this struct with the plugin-specific state
    2802             :  * @param denom_pub_hash hash of the public key used for signing coins of this denomination
    2803             :  * @param[out] issue set to issue information with value, fees and other info about the coin
    2804             :  * @return transaction status code
    2805             :  */
    2806             : static enum GNUNET_DB_QueryStatus
    2807           9 : postgres_get_denomination_info (
    2808             :   void *cls,
    2809             :   const struct GNUNET_HashCode *denom_pub_hash,
    2810             :   struct TALER_EXCHANGEDB_DenominationKeyInformationP *issue)
    2811             : {
    2812           9 :   struct PostgresClosure *pg = cls;
    2813             :   enum GNUNET_DB_QueryStatus qs;
    2814           9 :   struct GNUNET_PQ_QueryParam params[] = {
    2815           9 :     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
    2816             :     GNUNET_PQ_query_param_end
    2817             :   };
    2818           9 :   struct GNUNET_PQ_ResultSpec rs[] = {
    2819           9 :     GNUNET_PQ_result_spec_auto_from_type ("master_sig",
    2820             :                                           &issue->signature),
    2821           9 :     TALER_PQ_result_spec_absolute_time_nbo ("valid_from",
    2822             :                                             &issue->properties.start),
    2823           9 :     TALER_PQ_result_spec_absolute_time_nbo ("expire_withdraw",
    2824             :                                             &issue->properties.expire_withdraw),
    2825           9 :     TALER_PQ_result_spec_absolute_time_nbo ("expire_deposit",
    2826             :                                             &issue->properties.expire_deposit),
    2827           9 :     TALER_PQ_result_spec_absolute_time_nbo ("expire_legal",
    2828             :                                             &issue->properties.expire_legal),
    2829           9 :     TALER_PQ_RESULT_SPEC_AMOUNT_NBO ("coin",
    2830             :                                      &issue->properties.value),
    2831           9 :     TALER_PQ_RESULT_SPEC_AMOUNT_NBO ("fee_withdraw",
    2832             :                                      &issue->properties.fee_withdraw),
    2833           9 :     TALER_PQ_RESULT_SPEC_AMOUNT_NBO ("fee_deposit",
    2834             :                                      &issue->properties.fee_deposit),
    2835           9 :     TALER_PQ_RESULT_SPEC_AMOUNT_NBO ("fee_refresh",
    2836             :                                      &issue->properties.fee_refresh),
    2837           9 :     TALER_PQ_RESULT_SPEC_AMOUNT_NBO ("fee_refund",
    2838             :                                      &issue->properties.fee_refund),
    2839             :     GNUNET_PQ_result_spec_end
    2840             :   };
    2841             : 
    2842           9 :   memset (&issue->properties.master,
    2843             :           0,
    2844             :           sizeof (issue->properties.master));
    2845           9 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    2846             :                                                  "denomination_get",
    2847             :                                                  params,
    2848             :                                                  rs);
    2849           9 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    2850           1 :     return qs;
    2851           8 :   issue->properties.purpose.size = htonl (sizeof (struct
    2852             :                                                   TALER_DenominationKeyValidityPS));
    2853           8 :   issue->properties.purpose.purpose = htonl (
    2854             :     TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
    2855           8 :   issue->properties.denom_hash = *denom_pub_hash;
    2856           8 :   return qs;
    2857             : }
    2858             : 
    2859             : 
    2860             : /**
    2861             :  * Closure for #domination_cb_helper()
    2862             :  */
    2863             : struct DenomIteratorContext
    2864             : {
    2865             :   /**
    2866             :    * Function to call with the results.
    2867             :    */
    2868             :   TALER_EXCHANGEDB_DenominationCallback cb;
    2869             : 
    2870             :   /**
    2871             :    * Closure to pass to @e cb
    2872             :    */
    2873             :   void *cb_cls;
    2874             : 
    2875             :   /**
    2876             :    * Plugin context.
    2877             :    */
    2878             :   struct PostgresClosure *pg;
    2879             : };
    2880             : 
    2881             : 
    2882             : /**
    2883             :  * Helper function for #postgres_iterate_denomination_info().
    2884             :  * Calls the callback with each denomination key.
    2885             :  *
    2886             :  * @param cls a `struct DenomIteratorContext`
    2887             :  * @param result db results
    2888             :  * @param num_results number of results in @a result
    2889             :  */
    2890             : static void
    2891         183 : domination_cb_helper (void *cls,
    2892             :                       PGresult *result,
    2893             :                       unsigned int num_results)
    2894             : {
    2895         183 :   struct DenomIteratorContext *dic = cls;
    2896         183 :   struct PostgresClosure *pg = dic->pg;
    2897             : 
    2898       74543 :   for (unsigned int i = 0; i<num_results; i++)
    2899             :   {
    2900             :     struct TALER_EXCHANGEDB_DenominationKeyInformationP issue;
    2901             :     struct TALER_DenominationPublicKey denom_pub;
    2902       74360 :     struct GNUNET_PQ_ResultSpec rs[] = {
    2903       74360 :       GNUNET_PQ_result_spec_auto_from_type ("master_sig",
    2904             :                                             &issue.signature),
    2905       74360 :       TALER_PQ_result_spec_absolute_time_nbo ("valid_from",
    2906             :                                               &issue.properties.start),
    2907       74360 :       TALER_PQ_result_spec_absolute_time_nbo ("expire_withdraw",
    2908             :                                               &issue.properties.expire_withdraw),
    2909       74360 :       TALER_PQ_result_spec_absolute_time_nbo ("expire_deposit",
    2910             :                                               &issue.properties.expire_deposit),
    2911       74360 :       TALER_PQ_result_spec_absolute_time_nbo ("expire_legal",
    2912             :                                               &issue.properties.expire_legal),
    2913       74360 :       TALER_PQ_RESULT_SPEC_AMOUNT_NBO ("coin",
    2914             :                                        &issue.properties.value),
    2915       74360 :       TALER_PQ_RESULT_SPEC_AMOUNT_NBO ("fee_withdraw",
    2916             :                                        &issue.properties.fee_withdraw),
    2917       74360 :       TALER_PQ_RESULT_SPEC_AMOUNT_NBO ("fee_deposit",
    2918             :                                        &issue.properties.fee_deposit),
    2919       74360 :       TALER_PQ_RESULT_SPEC_AMOUNT_NBO ("fee_refresh",
    2920             :                                        &issue.properties.fee_refresh),
    2921       74360 :       TALER_PQ_RESULT_SPEC_AMOUNT_NBO ("fee_refund",
    2922             :                                        &issue.properties.fee_refund),
    2923       74360 :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    2924             :                                             &denom_pub.rsa_public_key),
    2925             :       GNUNET_PQ_result_spec_end
    2926             :     };
    2927             : 
    2928       74360 :     memset (&issue.properties.master,
    2929             :             0,
    2930             :             sizeof (issue.properties.master));
    2931       74360 :     if (GNUNET_OK !=
    2932       74360 :         GNUNET_PQ_extract_result (result,
    2933             :                                   rs,
    2934             :                                   i))
    2935             :     {
    2936           0 :       GNUNET_break (0);
    2937           0 :       return;
    2938             :     }
    2939             :     issue.properties.purpose.size
    2940       74360 :       = htonl (sizeof (struct TALER_DenominationKeyValidityPS));
    2941             :     issue.properties.purpose.purpose
    2942       74360 :       = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
    2943       74360 :     GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.rsa_public_key,
    2944             :                                        &issue.properties.denom_hash);
    2945       74360 :     dic->cb (dic->cb_cls,
    2946             :              &denom_pub,
    2947             :              &issue);
    2948       74360 :     GNUNET_CRYPTO_rsa_public_key_free (denom_pub.rsa_public_key);
    2949             :   }
    2950             : }
    2951             : 
    2952             : 
    2953             : /**
    2954             :  * Fetch information about all known denomination keys.
    2955             :  *
    2956             :  * @param cls the @e cls of this struct with the plugin-specific state
    2957             :  * @param cb function to call on each denomination key
    2958             :  * @param cb_cls closure for @a cb
    2959             :  * @return transaction status code
    2960             :  */
    2961             : static enum GNUNET_DB_QueryStatus
    2962         183 : postgres_iterate_denomination_info (void *cls,
    2963             :                                     TALER_EXCHANGEDB_DenominationCallback cb,
    2964             :                                     void *cb_cls)
    2965             : {
    2966         183 :   struct PostgresClosure *pg = cls;
    2967         183 :   struct GNUNET_PQ_QueryParam params[] = {
    2968             :     GNUNET_PQ_query_param_end
    2969             :   };
    2970         183 :   struct DenomIteratorContext dic = {
    2971             :     .cb = cb,
    2972             :     .cb_cls = cb_cls,
    2973             :     .pg = pg
    2974             :   };
    2975             : 
    2976         183 :   return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    2977             :                                                "denomination_iterate",
    2978             :                                                params,
    2979             :                                                &domination_cb_helper,
    2980             :                                                &dic);
    2981             : }
    2982             : 
    2983             : 
    2984             : /**
    2985             :  * Closure for #dominations_cb_helper()
    2986             :  */
    2987             : struct DenomsIteratorContext
    2988             : {
    2989             :   /**
    2990             :    * Function to call with the results.
    2991             :    */
    2992             :   TALER_EXCHANGEDB_DenominationsCallback cb;
    2993             : 
    2994             :   /**
    2995             :    * Closure to pass to @e cb
    2996             :    */
    2997             :   void *cb_cls;
    2998             : 
    2999             :   /**
    3000             :    * Plugin context.
    3001             :    */
    3002             :   struct PostgresClosure *pg;
    3003             : };
    3004             : 
    3005             : 
    3006             : /**
    3007             :  * Helper function for #postgres_iterate_denominations().
    3008             :  * Calls the callback with each denomination key.
    3009             :  *
    3010             :  * @param cls a `struct DenomsIteratorContext`
    3011             :  * @param result db results
    3012             :  * @param num_results number of results in @a result
    3013             :  */
    3014             : static void
    3015          52 : dominations_cb_helper (void *cls,
    3016             :                        PGresult *result,
    3017             :                        unsigned int num_results)
    3018             : {
    3019          52 :   struct DenomsIteratorContext *dic = cls;
    3020          52 :   struct PostgresClosure *pg = dic->pg;
    3021             : 
    3022         353 :   for (unsigned int i = 0; i<num_results; i++)
    3023             :   {
    3024             :     struct TALER_EXCHANGEDB_DenominationKeyMetaData meta;
    3025             :     struct TALER_DenominationPublicKey denom_pub;
    3026             :     struct TALER_MasterSignatureP master_sig;
    3027             :     struct GNUNET_HashCode h_denom_pub;
    3028             :     uint8_t revoked;
    3029         301 :     struct GNUNET_PQ_ResultSpec rs[] = {
    3030         301 :       GNUNET_PQ_result_spec_auto_from_type ("master_sig",
    3031             :                                             &master_sig),
    3032         301 :       GNUNET_PQ_result_spec_auto_from_type ("revoked",
    3033             :                                             &revoked),
    3034         301 :       TALER_PQ_result_spec_absolute_time ("valid_from",
    3035             :                                           &meta.start),
    3036         301 :       TALER_PQ_result_spec_absolute_time ("expire_withdraw",
    3037             :                                           &meta.expire_withdraw),
    3038         301 :       TALER_PQ_result_spec_absolute_time ("expire_deposit",
    3039             :                                           &meta.expire_deposit),
    3040         301 :       TALER_PQ_result_spec_absolute_time ("expire_legal",
    3041             :                                           &meta.expire_legal),
    3042         301 :       TALER_PQ_RESULT_SPEC_AMOUNT ("coin",
    3043             :                                    &meta.value),
    3044         301 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
    3045             :                                    &meta.fee_withdraw),
    3046         301 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    3047             :                                    &meta.fee_deposit),
    3048         301 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
    3049             :                                    &meta.fee_refresh),
    3050         301 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
    3051             :                                    &meta.fee_refund),
    3052         301 :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    3053             :                                             &denom_pub.rsa_public_key),
    3054             :       GNUNET_PQ_result_spec_end
    3055             :     };
    3056             : 
    3057         301 :     if (GNUNET_OK !=
    3058         301 :         GNUNET_PQ_extract_result (result,
    3059             :                                   rs,
    3060             :                                   i))
    3061             :     {
    3062           0 :       GNUNET_break (0);
    3063           0 :       return;
    3064             :     }
    3065         301 :     GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.rsa_public_key,
    3066             :                                        &h_denom_pub);
    3067         301 :     dic->cb (dic->cb_cls,
    3068             :              &denom_pub,
    3069             :              &h_denom_pub,
    3070             :              &meta,
    3071             :              &master_sig,
    3072             :              (0 != revoked));
    3073         301 :     GNUNET_PQ_cleanup_result (rs);
    3074             :   }
    3075             : }
    3076             : 
    3077             : 
    3078             : /**
    3079             : * Function called to invoke @a cb on every known denomination key (revoked
    3080             : * and non-revoked) that has been signed by the master key. Runs in its own
    3081             : * read-only transaction.
    3082             : *
    3083             : *
    3084             :  * @param cls the @e cls of this struct with the plugin-specific state
    3085             :  * @param cb function to call on each denomination key
    3086             :  * @param cb_cls closure for @a cb
    3087             :  * @return transaction status code
    3088             :  */
    3089             : static enum GNUNET_DB_QueryStatus
    3090          52 : postgres_iterate_denominations (void *cls,
    3091             :                                 TALER_EXCHANGEDB_DenominationsCallback cb,
    3092             :                                 void *cb_cls)
    3093             : {
    3094          52 :   struct PostgresClosure *pg = cls;
    3095          52 :   struct GNUNET_PQ_QueryParam params[] = {
    3096             :     GNUNET_PQ_query_param_end
    3097             :   };
    3098          52 :   struct DenomsIteratorContext dic = {
    3099             :     .cb = cb,
    3100             :     .cb_cls = cb_cls,
    3101             :     .pg = pg
    3102             :   };
    3103             : 
    3104          52 :   return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    3105             :                                                "select_denominations",
    3106             :                                                params,
    3107             :                                                &dominations_cb_helper,
    3108             :                                                &dic);
    3109             : }
    3110             : 
    3111             : 
    3112             : /**
    3113             :  * Closure for #signkeys_cb_helper()
    3114             :  */
    3115             : struct SignkeysIteratorContext
    3116             : {
    3117             :   /**
    3118             :    * Function to call with the results.
    3119             :    */
    3120             :   TALER_EXCHANGEDB_ActiveSignkeysCallback cb;
    3121             : 
    3122             :   /**
    3123             :    * Closure to pass to @e cb
    3124             :    */
    3125             :   void *cb_cls;
    3126             : 
    3127             : };
    3128             : 
    3129             : 
    3130             : /**
    3131             :  * Helper function for #postgres_iterate_active_signkeys().
    3132             :  * Calls the callback with each signkey.
    3133             :  *
    3134             :  * @param cls a `struct SignkeysIteratorContext`
    3135             :  * @param result db results
    3136             :  * @param num_results number of results in @a result
    3137             :  */
    3138             : static void
    3139          52 : signkeys_cb_helper (void *cls,
    3140             :                     PGresult *result,
    3141             :                     unsigned int num_results)
    3142             : {
    3143          52 :   struct SignkeysIteratorContext *dic = cls;
    3144             : 
    3145          82 :   for (unsigned int i = 0; i<num_results; i++)
    3146             :   {
    3147             :     struct TALER_EXCHANGEDB_SignkeyMetaData meta;
    3148             :     struct TALER_ExchangePublicKeyP exchange_pub;
    3149             :     struct TALER_MasterSignatureP master_sig;
    3150          30 :     struct GNUNET_PQ_ResultSpec rs[] = {
    3151          30 :       GNUNET_PQ_result_spec_auto_from_type ("master_sig",
    3152             :                                             &master_sig),
    3153          30 :       GNUNET_PQ_result_spec_auto_from_type ("exchange_pub",
    3154             :                                             &exchange_pub),
    3155          30 :       TALER_PQ_result_spec_absolute_time ("valid_from",
    3156             :                                           &meta.start),
    3157          30 :       TALER_PQ_result_spec_absolute_time ("expire_sign",
    3158             :                                           &meta.expire_sign),
    3159          30 :       TALER_PQ_result_spec_absolute_time ("expire_legal",
    3160             :                                           &meta.expire_legal),
    3161             :       GNUNET_PQ_result_spec_end
    3162             :     };
    3163             : 
    3164          30 :     if (GNUNET_OK !=
    3165          30 :         GNUNET_PQ_extract_result (result,
    3166             :                                   rs,
    3167             :                                   i))
    3168             :     {
    3169           0 :       GNUNET_break (0);
    3170           0 :       return;
    3171             :     }
    3172          30 :     dic->cb (dic->cb_cls,
    3173             :              &exchange_pub,
    3174             :              &meta,
    3175             :              &master_sig);
    3176             :   }
    3177             : }
    3178             : 
    3179             : 
    3180             : /**
    3181             :  * Function called to invoke @a cb on every non-revoked exchange signing key
    3182             :  * that has been signed by the master key.  Revoked and (for signing!)
    3183             :  * expired keys are skipped. Runs in its own read-only transaction.
    3184             :  *
    3185             :  * @param cls the @e cls of this struct with the plugin-specific state
    3186             :  * @param cb function to call on each signing key
    3187             :  * @param cb_cls closure for @a cb
    3188             :  * @return transaction status code
    3189             :  */
    3190             : static enum GNUNET_DB_QueryStatus
    3191          52 : postgres_iterate_active_signkeys (void *cls,
    3192             :                                   TALER_EXCHANGEDB_ActiveSignkeysCallback cb,
    3193             :                                   void *cb_cls)
    3194             : {
    3195          52 :   struct PostgresClosure *pg = cls;
    3196             :   struct GNUNET_TIME_Absolute now;
    3197          52 :   struct GNUNET_PQ_QueryParam params[] = {
    3198          52 :     GNUNET_PQ_query_param_absolute_time (&now),
    3199             :     GNUNET_PQ_query_param_end
    3200             :   };
    3201          52 :   struct SignkeysIteratorContext dic = {
    3202             :     .cb = cb,
    3203             :     .cb_cls = cb_cls,
    3204             :   };
    3205             : 
    3206          52 :   now = GNUNET_TIME_absolute_get ();
    3207          52 :   return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    3208             :                                                "select_signkeys",
    3209             :                                                params,
    3210             :                                                &signkeys_cb_helper,
    3211             :                                                &dic);
    3212             : }
    3213             : 
    3214             : 
    3215             : /**
    3216             :  * Closure for #auditors_cb_helper()
    3217             :  */
    3218             : struct AuditorsIteratorContext
    3219             : {
    3220             :   /**
    3221             :    * Function to call with the results.
    3222             :    */
    3223             :   TALER_EXCHANGEDB_AuditorsCallback cb;
    3224             : 
    3225             :   /**
    3226             :    * Closure to pass to @e cb
    3227             :    */
    3228             :   void *cb_cls;
    3229             : 
    3230             : };
    3231             : 
    3232             : 
    3233             : /**
    3234             :  * Helper function for #postgres_iterate_active_auditors().
    3235             :  * Calls the callback with each auditor.
    3236             :  *
    3237             :  * @param cls a `struct SignkeysIteratorContext`
    3238             :  * @param result db results
    3239             :  * @param num_results number of results in @a result
    3240             :  */
    3241             : static void
    3242          52 : auditors_cb_helper (void *cls,
    3243             :                     PGresult *result,
    3244             :                     unsigned int num_results)
    3245             : {
    3246          52 :   struct AuditorsIteratorContext *dic = cls;
    3247             : 
    3248          86 :   for (unsigned int i = 0; i<num_results; i++)
    3249             :   {
    3250             :     struct TALER_AuditorPublicKeyP auditor_pub;
    3251             :     char *auditor_url;
    3252             :     char *auditor_name;
    3253          34 :     struct GNUNET_PQ_ResultSpec rs[] = {
    3254          34 :       GNUNET_PQ_result_spec_auto_from_type ("auditor_pub",
    3255             :                                             &auditor_pub),
    3256          34 :       GNUNET_PQ_result_spec_string ("auditor_url",
    3257             :                                     &auditor_url),
    3258          34 :       GNUNET_PQ_result_spec_string ("auditor_name",
    3259             :                                     &auditor_name),
    3260             :       GNUNET_PQ_result_spec_end
    3261             :     };
    3262             : 
    3263          34 :     if (GNUNET_OK !=
    3264          34 :         GNUNET_PQ_extract_result (result,
    3265             :                                   rs,
    3266             :                                   i))
    3267             :     {
    3268           0 :       GNUNET_break (0);
    3269           0 :       return;
    3270             :     }
    3271          34 :     dic->cb (dic->cb_cls,
    3272             :              &auditor_pub,
    3273             :              auditor_url,
    3274             :              auditor_name);
    3275          34 :     GNUNET_PQ_cleanup_result (rs);
    3276             :   }
    3277             : }
    3278             : 
    3279             : 
    3280             : /**
    3281             :  * Function called to invoke @a cb on every active auditor. Disabled
    3282             :  * auditors are skipped. Runs in its own read-only transaction.
    3283             :   *
    3284             :  * @param cls the @e cls of this struct with the plugin-specific state
    3285             :  * @param cb function to call on each active auditor
    3286             :  * @param cb_cls closure for @a cb
    3287             :  * @return transaction status code
    3288             :  */
    3289             : static enum GNUNET_DB_QueryStatus
    3290          52 : postgres_iterate_active_auditors (void *cls,
    3291             :                                   TALER_EXCHANGEDB_AuditorsCallback cb,
    3292             :                                   void *cb_cls)
    3293             : {
    3294          52 :   struct PostgresClosure *pg = cls;
    3295          52 :   struct GNUNET_PQ_QueryParam params[] = {
    3296             :     GNUNET_PQ_query_param_end
    3297             :   };
    3298          52 :   struct AuditorsIteratorContext dic = {
    3299             :     .cb = cb,
    3300             :     .cb_cls = cb_cls,
    3301             :   };
    3302             : 
    3303          52 :   return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    3304             :                                                "select_auditors",
    3305             :                                                params,
    3306             :                                                &auditors_cb_helper,
    3307             :                                                &dic);
    3308             : }
    3309             : 
    3310             : 
    3311             : /**
    3312             :  * Closure for #auditor_denoms_cb_helper()
    3313             :  */
    3314             : struct AuditorDenomsIteratorContext
    3315             : {
    3316             :   /**
    3317             :    * Function to call with the results.
    3318             :    */
    3319             :   TALER_EXCHANGEDB_AuditorDenominationsCallback cb;
    3320             : 
    3321             :   /**
    3322             :    * Closure to pass to @e cb
    3323             :    */
    3324             :   void *cb_cls;
    3325             : };
    3326             : 
    3327             : 
    3328             : /**
    3329             :  * Helper function for #postgres_iterate_auditor_denominations().
    3330             :  * Calls the callback with each auditor and denomination pair.
    3331             :  *
    3332             :  * @param cls a `struct AuditorDenomsIteratorContext`
    3333             :  * @param result db results
    3334             :  * @param num_results number of results in @a result
    3335             :  */
    3336             : static void
    3337          52 : auditor_denoms_cb_helper (void *cls,
    3338             :                           PGresult *result,
    3339             :                           unsigned int num_results)
    3340             : {
    3341          52 :   struct AuditorDenomsIteratorContext *dic = cls;
    3342             : 
    3343          52 :   for (unsigned int i = 0; i<num_results; i++)
    3344             :   {
    3345             :     struct TALER_AuditorPublicKeyP auditor_pub;
    3346             :     struct GNUNET_HashCode h_denom_pub;
    3347             :     struct TALER_AuditorSignatureP auditor_sig;
    3348           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
    3349           0 :       GNUNET_PQ_result_spec_auto_from_type ("auditor_pub",
    3350             :                                             &auditor_pub),
    3351           0 :       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    3352             :                                             &h_denom_pub),
    3353           0 :       GNUNET_PQ_result_spec_auto_from_type ("auditor_sig",
    3354             :                                             &auditor_sig),
    3355             :       GNUNET_PQ_result_spec_end
    3356             :     };
    3357             : 
    3358           0 :     if (GNUNET_OK !=
    3359           0 :         GNUNET_PQ_extract_result (result,
    3360             :                                   rs,
    3361             :                                   i))
    3362             :     {
    3363           0 :       GNUNET_break (0);
    3364           0 :       return;
    3365             :     }
    3366           0 :     dic->cb (dic->cb_cls,
    3367             :              &auditor_pub,
    3368             :              &h_denom_pub,
    3369             :              &auditor_sig);
    3370             :   }
    3371             : }
    3372             : 
    3373             : 
    3374             : /**
    3375             :  * Function called to invoke @a cb on every denomination with an active
    3376             :  * auditor. Disabled auditors and denominations without auditor are
    3377             :  * skipped. Runs in its own read-only transaction.
    3378             :  *
    3379             :  * @param cls the @e cls of this struct with the plugin-specific state
    3380             :  * @param cb function to call on each active auditor
    3381             :  * @param cb_cls closure for @a cb
    3382             :  * @return transaction status code
    3383             :  */
    3384             : static enum GNUNET_DB_QueryStatus
    3385          52 : postgres_iterate_auditor_denominations (
    3386             :   void *cls,
    3387             :   TALER_EXCHANGEDB_AuditorDenominationsCallback cb,
    3388             :   void *cb_cls)
    3389             : {
    3390          52 :   struct PostgresClosure *pg = cls;
    3391          52 :   struct GNUNET_PQ_QueryParam params[] = {
    3392             :     GNUNET_PQ_query_param_end
    3393             :   };
    3394          52 :   struct AuditorDenomsIteratorContext dic = {
    3395             :     .cb = cb,
    3396             :     .cb_cls = cb_cls,
    3397             :   };
    3398             : 
    3399          52 :   return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    3400             :                                                "select_auditor_denoms",
    3401             :                                                params,
    3402             :                                                &auditor_denoms_cb_helper,
    3403             :                                                &dic);
    3404             : }
    3405             : 
    3406             : 
    3407             : /**
    3408             :  * Get the summary of a reserve.
    3409             :  *
    3410             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3411             :  * @param[in,out] reserve the reserve data.  The public key of the reserve should be
    3412             :  *          set in this structure; it is used to query the database.  The balance
    3413             :  *          and expiration are then filled accordingly.
    3414             :  * @return transaction status
    3415             :  */
    3416             : static enum GNUNET_DB_QueryStatus
    3417         193 : postgres_reserves_get (void *cls,
    3418             :                        struct TALER_EXCHANGEDB_Reserve *reserve)
    3419             : {
    3420         193 :   struct PostgresClosure *pg = cls;
    3421         193 :   struct GNUNET_PQ_QueryParam params[] = {
    3422         193 :     GNUNET_PQ_query_param_auto_from_type (&reserve->pub),
    3423             :     GNUNET_PQ_query_param_end
    3424             :   };
    3425         193 :   struct GNUNET_PQ_ResultSpec rs[] = {
    3426         193 :     TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance", &reserve->balance),
    3427         193 :     TALER_PQ_result_spec_absolute_time ("expiration_date", &reserve->expiry),
    3428         193 :     TALER_PQ_result_spec_absolute_time ("gc_date", &reserve->gc),
    3429             :     GNUNET_PQ_result_spec_end
    3430             :   };
    3431             : 
    3432         193 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    3433             :                                                    "reserves_get",
    3434             :                                                    params,
    3435             :                                                    rs);
    3436             : }
    3437             : 
    3438             : 
    3439             : /**
    3440             :  * Updates a reserve with the data from the given reserve structure.
    3441             :  *
    3442             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3443             :  * @param reserve the reserve structure whose data will be used to update the
    3444             :  *          corresponding record in the database.
    3445             :  * @return transaction status
    3446             :  */
    3447             : static enum GNUNET_DB_QueryStatus
    3448          74 : reserves_update (void *cls,
    3449             :                  const struct TALER_EXCHANGEDB_Reserve *reserve)
    3450             : {
    3451          74 :   struct PostgresClosure *pg = cls;
    3452          74 :   struct GNUNET_PQ_QueryParam params[] = {
    3453          74 :     TALER_PQ_query_param_absolute_time (&reserve->expiry),
    3454          74 :     TALER_PQ_query_param_absolute_time (&reserve->gc),
    3455          74 :     TALER_PQ_query_param_amount (&reserve->balance),
    3456          74 :     GNUNET_PQ_query_param_auto_from_type (&reserve->pub),
    3457             :     GNUNET_PQ_query_param_end
    3458             :   };
    3459             : 
    3460          74 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3461             :                                              "reserve_update",
    3462             :                                              params);
    3463             : }
    3464             : 
    3465             : 
    3466             : /**
    3467             :  * Generate event notification for the reserve
    3468             :  * change.
    3469             :  *
    3470             :  * @param pg plugin state
    3471             :  * @param reserve_pub reserve to notfiy on
    3472             :  */
    3473             : static void
    3474          28 : notify_on_reserve (struct PostgresClosure *pg,
    3475             :                    const struct TALER_ReservePublicKeyP *reserve_pub)
    3476             : {
    3477          28 :   struct TALER_ReserveEventP rep = {
    3478          28 :     .header.size = htons (sizeof (rep)),
    3479          28 :     .header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
    3480             :     .reserve_pub = *reserve_pub
    3481             :   };
    3482             : 
    3483          28 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3484             :               "Notifying on reserve!\n");
    3485          28 :   postgres_event_notify (pg,
    3486             :                          &rep.header,
    3487             :                          NULL,
    3488             :                          0);
    3489          28 : }
    3490             : 
    3491             : 
    3492             : /**
    3493             :  * Insert an incoming transaction into reserves.  New reserves are also created
    3494             :  * through this function.
    3495             :  *
    3496             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3497             :  * @param reserve_pub public key of the reserve
    3498             :  * @param balance the amount that has to be added to the reserve
    3499             :  * @param execution_time when was the amount added
    3500             :  * @param sender_account_details account information for the sender (payto://-URL)
    3501             :  * @param exchange_account_section name of the section in the configuration for the exchange's
    3502             :  *                       account into which the deposit was made
    3503             :  * @param wire_ref unique reference identifying the wire transfer
    3504             :  * @return transaction status code
    3505             :  */
    3506             : static enum GNUNET_DB_QueryStatus
    3507          28 : postgres_reserves_in_insert (void *cls,
    3508             :                              const struct TALER_ReservePublicKeyP *reserve_pub,
    3509             :                              const struct TALER_Amount *balance,
    3510             :                              struct GNUNET_TIME_Absolute execution_time,
    3511             :                              const char *sender_account_details,
    3512             :                              const char *exchange_account_section,
    3513             :                              uint64_t wire_ref)
    3514             : {
    3515          28 :   struct PostgresClosure *pg = cls;
    3516             :   enum GNUNET_DB_QueryStatus qs1;
    3517             :   struct TALER_EXCHANGEDB_Reserve reserve;
    3518             :   struct GNUNET_TIME_Absolute expiry;
    3519             :   struct GNUNET_TIME_Absolute gc;
    3520             :   struct GNUNET_TIME_Absolute now;
    3521             :   uint64_t reserve_uuid;
    3522             : 
    3523          28 :   now = GNUNET_TIME_absolute_get ();
    3524          28 :   (void) GNUNET_TIME_round_abs (&now);
    3525          28 :   reserve.pub = *reserve_pub;
    3526          28 :   expiry = GNUNET_TIME_absolute_add (execution_time,
    3527             :                                      pg->idle_reserve_expiration_time);
    3528          28 :   (void) GNUNET_TIME_round_abs (&expiry);
    3529          28 :   gc = GNUNET_TIME_absolute_add (now,
    3530             :                                  pg->legal_reserve_expiration_time);
    3531          28 :   (void) GNUNET_TIME_round_abs (&gc);
    3532          28 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3533             :               "Creating reserve %s with expiration in %s\n",
    3534             :               TALER_B2S (reserve_pub),
    3535             :               GNUNET_STRINGS_relative_time_to_string (
    3536             :                 pg->idle_reserve_expiration_time,
    3537             :                 GNUNET_NO));
    3538             :   /* Optimistically assume this is a new reserve, create balance for the first
    3539             :      time; we do this before adding the actual transaction to "reserves_in",
    3540             :      as for a new reserve it can't be a duplicate 'add' operation, and as
    3541             :      the 'add' operation may need the reserve entry as a foreign key. */
    3542             :   {
    3543          28 :     struct GNUNET_PQ_QueryParam params[] = {
    3544          28 :       GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    3545          28 :       GNUNET_PQ_query_param_string (sender_account_details),
    3546          28 :       TALER_PQ_query_param_amount (balance),
    3547          28 :       TALER_PQ_query_param_absolute_time (&expiry),
    3548          28 :       TALER_PQ_query_param_absolute_time (&gc),
    3549             :       GNUNET_PQ_query_param_end
    3550             :     };
    3551          28 :     struct GNUNET_PQ_ResultSpec rs[] = {
    3552          28 :       GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
    3553             :                                     &reserve_uuid),
    3554             :       GNUNET_PQ_result_spec_end
    3555             :     };
    3556             : 
    3557          28 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3558             :                 "Reserve does not exist; creating a new one\n");
    3559             :     /* Note: query uses 'on conflict do nothing' */
    3560          28 :     qs1 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    3561             :                                                     "reserve_create",
    3562             :                                                     params,
    3563             :                                                     rs);
    3564          28 :     if (qs1 < 0)
    3565           0 :       return qs1;
    3566             :   }
    3567             : 
    3568             :   /* Create new incoming transaction, "ON CONFLICT DO NOTHING"
    3569             :      is again used to guard against duplicates. */
    3570             :   {
    3571             :     enum GNUNET_DB_QueryStatus qs2;
    3572             : 
    3573          28 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs1)
    3574             :     {
    3575           1 :       struct GNUNET_PQ_QueryParam params[] = {
    3576           1 :         GNUNET_PQ_query_param_auto_from_type (&reserve.pub),
    3577           1 :         GNUNET_PQ_query_param_uint64 (&wire_ref),
    3578           1 :         TALER_PQ_query_param_amount (balance),
    3579           1 :         GNUNET_PQ_query_param_string (exchange_account_section),
    3580           1 :         GNUNET_PQ_query_param_string (sender_account_details),
    3581           1 :         TALER_PQ_query_param_absolute_time (&execution_time),
    3582             :         GNUNET_PQ_query_param_end
    3583             :       };
    3584             : 
    3585           1 :       qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3586             :                                                 "reserves_in_add_transaction",
    3587             :                                                 params);
    3588             :     }
    3589             :     else
    3590             :     {
    3591          27 :       struct GNUNET_PQ_QueryParam params[] = {
    3592          27 :         GNUNET_PQ_query_param_uint64 (&reserve_uuid),
    3593          27 :         GNUNET_PQ_query_param_uint64 (&wire_ref),
    3594          27 :         TALER_PQ_query_param_amount (balance),
    3595          27 :         GNUNET_PQ_query_param_string (exchange_account_section),
    3596          27 :         GNUNET_PQ_query_param_string (sender_account_details),
    3597          27 :         TALER_PQ_query_param_absolute_time (&execution_time),
    3598             :         GNUNET_PQ_query_param_end
    3599             :       };
    3600             : 
    3601          27 :       qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3602             :                                                 "reserves_in_add_by_uuid",
    3603             :                                                 params);
    3604             :     }
    3605          28 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs2)
    3606             :     {
    3607           0 :       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs2);
    3608           0 :       return qs2;
    3609             :     }
    3610          28 :     if (0 >= qs2)
    3611             :     {
    3612           0 :       if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs2) &&
    3613             :            (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1) )
    3614             :       {
    3615           0 :         GNUNET_break (0); /* should be impossible: reserve was fresh,
    3616             :                              but transaction already known */
    3617           0 :         return GNUNET_DB_STATUS_HARD_ERROR;
    3618             :       }
    3619             :       /* Transaction was already known or error. We are finished. */
    3620           0 :       return qs2;
    3621             :     }
    3622             :   }
    3623          28 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs1)
    3624             :   {
    3625          27 :     notify_on_reserve (pg,
    3626             :                        reserve_pub);
    3627          27 :     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* new reserve, we are finished */
    3628             :   }
    3629             : 
    3630             :   /* we were wrong with our optimistic assumption:
    3631             :      reserve does exist, need to do an update instead */
    3632             :   {
    3633             :     enum GNUNET_DB_QueryStatus cs;
    3634             : 
    3635           1 :     cs = postgres_commit (pg);
    3636           1 :     if (cs < 0)
    3637           0 :       return cs;
    3638           1 :     if (GNUNET_OK !=
    3639           1 :         postgres_start (pg,
    3640             :                         "reserve-update-serializable"))
    3641             :     {
    3642           0 :       GNUNET_break (0);
    3643           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    3644             :     }
    3645             :   }
    3646             :   {
    3647             :     enum GNUNET_DB_QueryStatus reserve_exists;
    3648             : 
    3649           1 :     reserve_exists = postgres_reserves_get (pg,
    3650             :                                             &reserve);
    3651           1 :     switch (reserve_exists)
    3652             :     {
    3653           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    3654           0 :       GNUNET_break (0);
    3655           0 :       return reserve_exists;
    3656           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    3657           0 :       return reserve_exists;
    3658           0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    3659             :       /* First we got a conflict, but then we cannot select? Very strange. */
    3660           0 :       GNUNET_break (0);
    3661           0 :       return GNUNET_DB_STATUS_SOFT_ERROR;
    3662           1 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    3663             :       /* continued below */
    3664           1 :       break;
    3665             :     }
    3666           1 :   }
    3667             : 
    3668             :   {
    3669             :     struct TALER_EXCHANGEDB_Reserve updated_reserve;
    3670             :     enum GNUNET_DB_QueryStatus qs3;
    3671             : 
    3672             :     /* If the reserve already existed, we need to still update the
    3673             :        balance; we do this after checking for duplication, as
    3674             :        otherwise we might have to actually pay the cost to roll this
    3675             :        back for duplicate transactions; like this, we should virtually
    3676             :        never actually have to rollback anything. */
    3677           1 :     updated_reserve.pub = reserve.pub;
    3678           1 :     if (0 >
    3679           1 :         TALER_amount_add (&updated_reserve.balance,
    3680             :                           &reserve.balance,
    3681             :                           balance))
    3682             :     {
    3683             :       /* currency overflow or incompatible currency */
    3684           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3685             :                   "Attempt to deposit incompatible amount into reserve\n");
    3686           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    3687             :     }
    3688           1 :     updated_reserve.expiry = GNUNET_TIME_absolute_max (expiry,
    3689             :                                                        reserve.expiry);
    3690           1 :     (void) GNUNET_TIME_round_abs (&updated_reserve.expiry);
    3691           1 :     updated_reserve.gc = GNUNET_TIME_absolute_max (gc,
    3692             :                                                    reserve.gc);
    3693           1 :     (void) GNUNET_TIME_round_abs (&updated_reserve.gc);
    3694           1 :     qs3 = reserves_update (pg,
    3695             :                            &updated_reserve);
    3696           1 :     switch (qs3)
    3697             :     {
    3698           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    3699           0 :       GNUNET_break (0);
    3700           0 :       return qs3;
    3701           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    3702           0 :       return qs3;
    3703           0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    3704             :       /* How can the UPDATE not work here? Very strange. */
    3705           0 :       GNUNET_break (0);
    3706           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    3707           1 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    3708             :       /* continued below */
    3709           1 :       break;
    3710             :     }
    3711           1 :   }
    3712           1 :   notify_on_reserve (pg,
    3713             :                      reserve_pub);
    3714             :   /* Go back to original transaction mode */
    3715             :   {
    3716             :     enum GNUNET_DB_QueryStatus cs;
    3717             : 
    3718           1 :     cs = postgres_commit (pg);
    3719           1 :     if (cs < 0)
    3720           0 :       return cs;
    3721           1 :     if (GNUNET_OK !=
    3722           1 :         postgres_start_read_committed (pg,
    3723             :                                        "reserve-insert-continued"))
    3724             :     {
    3725           0 :       GNUNET_break (0);
    3726           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    3727             :     }
    3728             :   }
    3729           1 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    3730             : }
    3731             : 
    3732             : 
    3733             : /**
    3734             :  * Obtain the most recent @a wire_reference that was inserted via @e reserves_in_insert.
    3735             :  *
    3736             :  * @param cls the @e cls of this struct with the plugin-specific state
    3737             :  * @param exchange_account_name name of the section in the exchange's configuration
    3738             :  *                       for the account that we are tracking here
    3739             :  * @param[out] wire_reference set to unique reference identifying the wire transfer
    3740             :  * @return transaction status code
    3741             :  */
    3742             : static enum GNUNET_DB_QueryStatus
    3743           4 : postgres_get_latest_reserve_in_reference (
    3744             :   void *cls,
    3745             :   const char *exchange_account_name,
    3746             :   uint64_t *wire_reference)
    3747             : {
    3748           4 :   struct PostgresClosure *pg = cls;
    3749           4 :   struct GNUNET_PQ_QueryParam params[] = {
    3750           4 :     GNUNET_PQ_query_param_string (exchange_account_name),
    3751             :     GNUNET_PQ_query_param_end
    3752             :   };
    3753           4 :   struct GNUNET_PQ_ResultSpec rs[] = {
    3754           4 :     GNUNET_PQ_result_spec_uint64 ("wire_reference",
    3755             :                                   wire_reference),
    3756             :     GNUNET_PQ_result_spec_end
    3757             :   };
    3758             : 
    3759           4 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    3760             :                                                    "reserves_in_get_latest_wire_reference",
    3761             :                                                    params,
    3762             :                                                    rs);
    3763             : }
    3764             : 
    3765             : 
    3766             : /**
    3767             :  * Locate the response for a /reserve/withdraw request under the
    3768             :  * key of the hash of the blinded message.
    3769             :  *
    3770             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3771             :  * @param h_blind hash of the blinded coin to be signed (will match
    3772             :  *                `h_coin_envelope` in the @a collectable to be returned)
    3773             :  * @param collectable corresponding collectable coin (blind signature)
    3774             :  *                    if a coin is found
    3775             :  * @return statement execution status
    3776             :  */
    3777             : static enum GNUNET_DB_QueryStatus
    3778          39 : postgres_get_withdraw_info (
    3779             :   void *cls,
    3780             :   const struct GNUNET_HashCode *h_blind,
    3781             :   struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
    3782             : {
    3783          39 :   struct PostgresClosure *pg = cls;
    3784          39 :   struct GNUNET_PQ_QueryParam params[] = {
    3785          39 :     GNUNET_PQ_query_param_auto_from_type (h_blind),
    3786             :     GNUNET_PQ_query_param_end
    3787             :   };
    3788          39 :   struct GNUNET_PQ_ResultSpec rs[] = {
    3789          39 :     GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    3790             :                                           &collectable->denom_pub_hash),
    3791          39 :     GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    3792             :                                          &collectable->sig.rsa_signature),
    3793          39 :     GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
    3794             :                                           &collectable->reserve_sig),
    3795          39 :     GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    3796             :                                           &collectable->reserve_pub),
    3797          39 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    3798             :                                  &collectable->amount_with_fee),
    3799          39 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
    3800             :                                  &collectable->withdraw_fee),
    3801             :     GNUNET_PQ_result_spec_end
    3802             :   };
    3803             : #if EXPLICIT_LOCKS
    3804             :   struct GNUNET_PQ_QueryParam no_params[] = {
    3805             :     GNUNET_PQ_query_param_end
    3806             :   };
    3807             :   enum GNUNET_DB_QueryStatus qs;
    3808             : 
    3809             :   if (0 > (qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3810             :                                                     "lock_withdraw",
    3811             :                                                     no_params)))
    3812             :     return qs;
    3813             : #endif
    3814          39 :   collectable->h_coin_envelope = *h_blind;
    3815          39 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    3816             :                                                    "get_withdraw_info",
    3817             :                                                    params,
    3818             :                                                    rs);
    3819             : }
    3820             : 
    3821             : 
    3822             : /**
    3823             :  * Store collectable bit coin under the corresponding
    3824             :  * hash of the blinded message.
    3825             :  *
    3826             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3827             :  * @param collectable corresponding collectable coin (blind signature)
    3828             :  *                    if a coin is found
    3829             :  * @return query execution status
    3830             :  */
    3831             : static enum GNUNET_DB_QueryStatus
    3832          36 : postgres_insert_withdraw_info (
    3833             :   void *cls,
    3834             :   const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
    3835             : {
    3836          36 :   struct PostgresClosure *pg = cls;
    3837             :   struct TALER_EXCHANGEDB_Reserve reserve;
    3838             :   struct GNUNET_TIME_Absolute now;
    3839             :   struct GNUNET_TIME_Absolute gc;
    3840          36 :   struct GNUNET_PQ_QueryParam params[] = {
    3841          36 :     GNUNET_PQ_query_param_auto_from_type (&collectable->h_coin_envelope),
    3842          36 :     GNUNET_PQ_query_param_auto_from_type (&collectable->denom_pub_hash),
    3843          36 :     GNUNET_PQ_query_param_rsa_signature (collectable->sig.rsa_signature),
    3844          36 :     GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_pub),
    3845          36 :     GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_sig),
    3846          36 :     TALER_PQ_query_param_absolute_time (&now),
    3847          36 :     TALER_PQ_query_param_amount (&collectable->amount_with_fee),
    3848             :     GNUNET_PQ_query_param_end
    3849             :   };
    3850             :   enum GNUNET_DB_QueryStatus qs;
    3851             : 
    3852          36 :   now = GNUNET_TIME_absolute_get ();
    3853          36 :   (void) GNUNET_TIME_round_abs (&now);
    3854          36 :   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3855             :                                            "insert_withdraw_info",
    3856             :                                            params);
    3857          36 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    3858             :   {
    3859           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    3860           0 :     return qs;
    3861             :   }
    3862             : 
    3863             :   /* update reserve balance */
    3864          36 :   reserve.pub = collectable->reserve_pub;
    3865          36 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    3866          36 :       (qs = postgres_reserves_get (pg,
    3867             :                                    &reserve)))
    3868             :   {
    3869             :     /* Should have been checked before we got here... */
    3870           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    3871           0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    3872           0 :       qs = GNUNET_DB_STATUS_HARD_ERROR;
    3873           0 :     return qs;
    3874             :   }
    3875          36 :   if (0 >
    3876          36 :       TALER_amount_subtract (&reserve.balance,
    3877             :                              &reserve.balance,
    3878             :                              &collectable->amount_with_fee))
    3879             :   {
    3880             :     /* The reserve history was checked to make sure there is enough of a balance
    3881             :        left before we tried this; however, concurrent operations may have changed
    3882             :        the situation by now, causing us to fail here. As reserves can no longer
    3883             :        be topped up, retrying should not help either.  */
    3884           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3885             :                 "Withdrawal from reserve `%s' refused due to insufficient balance.\n",
    3886             :                 TALER_B2S (&collectable->reserve_pub));
    3887           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    3888             :   }
    3889          36 :   gc = GNUNET_TIME_absolute_add (now,
    3890             :                                  pg->legal_reserve_expiration_time);
    3891          36 :   reserve.gc = GNUNET_TIME_absolute_max (gc,
    3892             :                                          reserve.gc);
    3893          36 :   (void) GNUNET_TIME_round_abs (&reserve.gc);
    3894          36 :   qs = reserves_update (pg,
    3895             :                         &reserve);
    3896          36 :   GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
    3897          36 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    3898             :   {
    3899           0 :     GNUNET_break (0);
    3900           0 :     qs = GNUNET_DB_STATUS_HARD_ERROR;
    3901             :   }
    3902          36 :   return qs;
    3903             : }
    3904             : 
    3905             : 
    3906             : /**
    3907             :  * Closure for callbacks invoked via #postgres_get_reserve_history.
    3908             :  */
    3909             : struct ReserveHistoryContext
    3910             : {
    3911             : 
    3912             :   /**
    3913             :    * Which reserve are we building the history for?
    3914             :    */
    3915             :   const struct TALER_ReservePublicKeyP *reserve_pub;
    3916             : 
    3917             :   /**
    3918             :    * Where we build the history.
    3919             :    */
    3920             :   struct TALER_EXCHANGEDB_ReserveHistory *rh;
    3921             : 
    3922             :   /**
    3923             :    * Tail of @e rh list.
    3924             :    */
    3925             :   struct TALER_EXCHANGEDB_ReserveHistory *rh_tail;
    3926             : 
    3927             :   /**
    3928             :    * Plugin context.
    3929             :    */
    3930             :   struct PostgresClosure *pg;
    3931             : 
    3932             :   /**
    3933             :    * Set to #GNUNET_SYSERR on serious internal errors during
    3934             :    * the callbacks.
    3935             :    */
    3936             :   int status;
    3937             : };
    3938             : 
    3939             : 
    3940             : /**
    3941             :  * Append and return a fresh element to the reserve
    3942             :  * history kept in @a rhc.
    3943             :  *
    3944             :  * @param rhc where the history is kept
    3945             :  * @return the fresh element that was added
    3946             :  */
    3947             : static struct TALER_EXCHANGEDB_ReserveHistory *
    3948          39 : append_rh (struct ReserveHistoryContext *rhc)
    3949             : {
    3950             :   struct TALER_EXCHANGEDB_ReserveHistory *tail;
    3951             : 
    3952          39 :   tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory);
    3953          39 :   if (NULL != rhc->rh_tail)
    3954             :   {
    3955          27 :     rhc->rh_tail->next = tail;
    3956          27 :     rhc->rh_tail = tail;
    3957             :   }
    3958             :   else
    3959             :   {
    3960          12 :     rhc->rh_tail = tail;
    3961          12 :     rhc->rh = tail;
    3962             :   }
    3963          39 :   return tail;
    3964             : }
    3965             : 
    3966             : 
    3967             : /**
    3968             :  * Add bank transfers to result set for #postgres_get_reserve_history.
    3969             :  *
    3970             :  * @param cls a `struct ReserveHistoryContext *`
    3971             :  * @param result SQL result
    3972             :  * @param num_results number of rows in @a result
    3973             :  */
    3974             : static void
    3975          12 : add_bank_to_exchange (void *cls,
    3976             :                       PGresult *result,
    3977             :                       unsigned int num_results)
    3978             : {
    3979          12 :   struct ReserveHistoryContext *rhc = cls;
    3980          12 :   struct PostgresClosure *pg = rhc->pg;
    3981             : 
    3982          25 :   while (0 < num_results)
    3983             :   {
    3984             :     struct TALER_EXCHANGEDB_BankTransfer *bt;
    3985             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    3986             : 
    3987          13 :     bt = GNUNET_new (struct TALER_EXCHANGEDB_BankTransfer);
    3988             :     {
    3989          13 :       struct GNUNET_PQ_ResultSpec rs[] = {
    3990          13 :         GNUNET_PQ_result_spec_uint64 ("wire_reference",
    3991             :                                       &bt->wire_reference),
    3992          13 :         TALER_PQ_RESULT_SPEC_AMOUNT ("credit",
    3993             :                                      &bt->amount),
    3994          13 :         TALER_PQ_result_spec_absolute_time ("execution_date",
    3995             :                                             &bt->execution_date),
    3996          13 :         GNUNET_PQ_result_spec_string ("sender_account_details",
    3997             :                                       &bt->sender_account_details),
    3998             :         GNUNET_PQ_result_spec_end
    3999             :       };
    4000             : 
    4001          13 :       if (GNUNET_OK !=
    4002          13 :           GNUNET_PQ_extract_result (result,
    4003             :                                     rs,
    4004             :                                     --num_results))
    4005             :       {
    4006           0 :         GNUNET_break (0);
    4007           0 :         GNUNET_free (bt);
    4008           0 :         rhc->status = GNUNET_SYSERR;
    4009           0 :         return;
    4010             :       }
    4011             :     }
    4012          13 :     bt->reserve_pub = *rhc->reserve_pub;
    4013          13 :     tail = append_rh (rhc);
    4014          13 :     tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE;
    4015          13 :     tail->details.bank = bt;
    4016             :   }   /* end of 'while (0 < rows)' */
    4017             : }
    4018             : 
    4019             : 
    4020             : /**
    4021             :  * Add coin withdrawals to result set for #postgres_get_reserve_history.
    4022             :  *
    4023             :  * @param cls a `struct ReserveHistoryContext *`
    4024             :  * @param result SQL result
    4025             :  * @param num_results number of rows in @a result
    4026             :  */
    4027             : static void
    4028          12 : add_withdraw_coin (void *cls,
    4029             :                    PGresult *result,
    4030             :                    unsigned int num_results)
    4031             : {
    4032          12 :   struct ReserveHistoryContext *rhc = cls;
    4033          12 :   struct PostgresClosure *pg = rhc->pg;
    4034             : 
    4035          27 :   while (0 < num_results)
    4036             :   {
    4037             :     struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc;
    4038             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    4039             : 
    4040          15 :     cbc = GNUNET_new (struct TALER_EXCHANGEDB_CollectableBlindcoin);
    4041             :     {
    4042          15 :       struct GNUNET_PQ_ResultSpec rs[] = {
    4043          15 :         GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
    4044             :                                               &cbc->h_coin_envelope),
    4045          15 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    4046             :                                               &cbc->denom_pub_hash),
    4047          15 :         GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    4048             :                                              &cbc->sig.rsa_signature),
    4049          15 :         GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
    4050             :                                               &cbc->reserve_sig),
    4051          15 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    4052             :                                      &cbc->amount_with_fee),
    4053          15 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
    4054             :                                      &cbc->withdraw_fee),
    4055             :         GNUNET_PQ_result_spec_end
    4056             :       };
    4057             : 
    4058          15 :       if (GNUNET_OK !=
    4059          15 :           GNUNET_PQ_extract_result (result,
    4060             :                                     rs,
    4061             :                                     --num_results))
    4062             :       {
    4063           0 :         GNUNET_break (0);
    4064           0 :         GNUNET_free (cbc);
    4065           0 :         rhc->status = GNUNET_SYSERR;
    4066           0 :         return;
    4067             :       }
    4068             :     }
    4069          15 :     cbc->reserve_pub = *rhc->reserve_pub;
    4070          15 :     tail = append_rh (rhc);
    4071          15 :     tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN;
    4072          15 :     tail->details.withdraw = cbc;
    4073             :   }
    4074             : }
    4075             : 
    4076             : 
    4077             : /**
    4078             :  * Add recoups to result set for #postgres_get_reserve_history.
    4079             :  *
    4080             :  * @param cls a `struct ReserveHistoryContext *`
    4081             :  * @param result SQL result
    4082             :  * @param num_results number of rows in @a result
    4083             :  */
    4084             : static void
    4085          12 : add_recoup (void *cls,
    4086             :             PGresult *result,
    4087             :             unsigned int num_results)
    4088             : {
    4089          12 :   struct ReserveHistoryContext *rhc = cls;
    4090          12 :   struct PostgresClosure *pg = rhc->pg;
    4091             : 
    4092          17 :   while (0 < num_results)
    4093             :   {
    4094             :     struct TALER_EXCHANGEDB_Recoup *recoup;
    4095             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    4096             : 
    4097           5 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_Recoup);
    4098             :     {
    4099           5 :       struct GNUNET_PQ_ResultSpec rs[] = {
    4100           5 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    4101             :                                      &recoup->value),
    4102           5 :         GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    4103             :                                               &recoup->coin.coin_pub),
    4104           5 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
    4105             :                                               &recoup->coin_blind),
    4106           5 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    4107             :                                               &recoup->coin_sig),
    4108           5 :         TALER_PQ_result_spec_absolute_time ("timestamp",
    4109             :                                             &recoup->timestamp),
    4110           5 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    4111             :                                               &recoup->coin.denom_pub_hash),
    4112           5 :         GNUNET_PQ_result_spec_rsa_signature (
    4113             :           "denom_sig",
    4114             :           &recoup->coin.denom_sig.rsa_signature),
    4115             :         GNUNET_PQ_result_spec_end
    4116             :       };
    4117             : 
    4118           5 :       if (GNUNET_OK !=
    4119           5 :           GNUNET_PQ_extract_result (result,
    4120             :                                     rs,
    4121             :                                     --num_results))
    4122             :       {
    4123           0 :         GNUNET_break (0);
    4124           0 :         GNUNET_free (recoup);
    4125           0 :         rhc->status = GNUNET_SYSERR;
    4126           0 :         return;
    4127             :       }
    4128             :     }
    4129           5 :     recoup->reserve_pub = *rhc->reserve_pub;
    4130           5 :     tail = append_rh (rhc);
    4131           5 :     tail->type = TALER_EXCHANGEDB_RO_RECOUP_COIN;
    4132           5 :     tail->details.recoup = recoup;
    4133             :   }   /* end of 'while (0 < rows)' */
    4134             : }
    4135             : 
    4136             : 
    4137             : /**
    4138             :  * Add exchange-to-bank transfers to result set for
    4139             :  * #postgres_get_reserve_history.
    4140             :  *
    4141             :  * @param cls a `struct ReserveHistoryContext *`
    4142             :  * @param result SQL result
    4143             :  * @param num_results number of rows in @a result
    4144             :  */
    4145             : static void
    4146          12 : add_exchange_to_bank (void *cls,
    4147             :                       PGresult *result,
    4148             :                       unsigned int num_results)
    4149             : {
    4150          12 :   struct ReserveHistoryContext *rhc = cls;
    4151          12 :   struct PostgresClosure *pg = rhc->pg;
    4152             : 
    4153          18 :   while (0 < num_results)
    4154             :   {
    4155             :     struct TALER_EXCHANGEDB_ClosingTransfer *closing;
    4156             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    4157             : 
    4158           6 :     closing = GNUNET_new (struct TALER_EXCHANGEDB_ClosingTransfer);
    4159             :     {
    4160           6 :       struct GNUNET_PQ_ResultSpec rs[] = {
    4161           6 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    4162             :                                      &closing->amount),
    4163           6 :         TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
    4164             :                                      &closing->closing_fee),
    4165           6 :         TALER_PQ_result_spec_absolute_time ("execution_date",
    4166             :                                             &closing->execution_date),
    4167           6 :         GNUNET_PQ_result_spec_string ("receiver_account",
    4168             :                                       &closing->receiver_account_details),
    4169           6 :         GNUNET_PQ_result_spec_auto_from_type ("wtid",
    4170             :                                               &closing->wtid),
    4171             :         GNUNET_PQ_result_spec_end
    4172             :       };
    4173             : 
    4174           6 :       if (GNUNET_OK !=
    4175           6 :           GNUNET_PQ_extract_result (result,
    4176             :                                     rs,
    4177             :                                     --num_results))
    4178             :       {
    4179           0 :         GNUNET_break (0);
    4180           0 :         GNUNET_free (closing);
    4181           0 :         rhc->status = GNUNET_SYSERR;
    4182           0 :         return;
    4183             :       }
    4184             :     }
    4185           6 :     closing->reserve_pub = *rhc->reserve_pub;
    4186           6 :     tail = append_rh (rhc);
    4187           6 :     tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK;
    4188           6 :     tail->details.closing = closing;
    4189             :   }   /* end of 'while (0 < rows)' */
    4190             : }
    4191             : 
    4192             : 
    4193             : /**
    4194             :  * Get all of the transaction history associated with the specified
    4195             :  * reserve.
    4196             :  *
    4197             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    4198             :  * @param reserve_pub public key of the reserve
    4199             :  * @param[out] rhp set to known transaction history (NULL if reserve is unknown)
    4200             :  * @return transaction status
    4201             :  */
    4202             : static enum GNUNET_DB_QueryStatus
    4203          12 : postgres_get_reserve_history (void *cls,
    4204             :                               const struct TALER_ReservePublicKeyP *reserve_pub,
    4205             :                               struct TALER_EXCHANGEDB_ReserveHistory **rhp)
    4206             : {
    4207          12 :   struct PostgresClosure *pg = cls;
    4208             :   struct ReserveHistoryContext rhc;
    4209             :   struct
    4210             :   {
    4211             :     /**
    4212             :      * Name of the prepared statement to run.
    4213             :      */
    4214             :     const char *statement;
    4215             :     /**
    4216             :      * Function to use to process the results.
    4217             :      */
    4218             :     GNUNET_PQ_PostgresResultHandler cb;
    4219          12 :   } work[] = {
    4220             :     /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */
    4221             :     { "reserves_in_get_transactions",
    4222             :       add_bank_to_exchange },
    4223             :     /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */
    4224             :     { "get_reserves_out",
    4225             :       &add_withdraw_coin },
    4226             :     /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */
    4227             :     { "recoup_by_reserve",
    4228             :       &add_recoup },
    4229             :     /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */
    4230             :     { "close_by_reserve",
    4231             :       &add_exchange_to_bank },
    4232             :     /* List terminator */
    4233             :     { NULL,
    4234             :       NULL }
    4235             :   };
    4236             :   enum GNUNET_DB_QueryStatus qs;
    4237          12 :   struct GNUNET_PQ_QueryParam params[] = {
    4238          12 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    4239             :     GNUNET_PQ_query_param_end
    4240             :   };
    4241             : 
    4242          12 :   rhc.reserve_pub = reserve_pub;
    4243          12 :   rhc.rh = NULL;
    4244          12 :   rhc.rh_tail = NULL;
    4245          12 :   rhc.pg = pg;
    4246          12 :   rhc.status = GNUNET_OK;
    4247          12 :   qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */
    4248          60 :   for (unsigned int i = 0; NULL != work[i].cb; i++)
    4249             :   {
    4250          48 :     qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    4251             :                                                work[i].statement,
    4252             :                                                params,
    4253             :                                                work[i].cb,
    4254             :                                                &rhc);
    4255          48 :     if ( (0 > qs) ||
    4256          48 :          (GNUNET_OK != rhc.status) )
    4257             :       break;
    4258             :   }
    4259          12 :   if ( (qs < 0) ||
    4260          12 :        (rhc.status != GNUNET_OK) )
    4261             :   {
    4262           0 :     common_free_reserve_history (cls,
    4263             :                                  rhc.rh);
    4264           0 :     rhc.rh = NULL;
    4265           0 :     if (qs >= 0)
    4266             :     {
    4267             :       /* status == SYSERR is a very hard error... */
    4268           0 :       qs = GNUNET_DB_STATUS_HARD_ERROR;
    4269             :     }
    4270             :   }
    4271          12 :   *rhp = rhc.rh;
    4272          12 :   return qs;
    4273             : }
    4274             : 
    4275             : 
    4276             : /**
    4277             :  * Check if we have the specified deposit already in the database.
    4278             :  *
    4279             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    4280             :  * @param deposit deposit to search for
    4281             :  * @param check_extras whether to check extra fields match or not
    4282             :  * @param[out] deposit_fee set to the deposit fee the exchange charged
    4283             :  * @param[out] exchange_timestamp set to the time when the exchange received the deposit
    4284             :  * @return 1 if we know this operation,
    4285             :  *         0 if this exact deposit is unknown to us,
    4286             :  *         otherwise transaction error status
    4287             :  */
    4288             : static enum GNUNET_DB_QueryStatus
    4289         199 : postgres_have_deposit (void *cls,
    4290             :                        const struct TALER_EXCHANGEDB_Deposit *deposit,
    4291             :                        int check_extras,
    4292             :                        struct TALER_Amount *deposit_fee,
    4293             :                        struct GNUNET_TIME_Absolute *exchange_timestamp)
    4294             : {
    4295         199 :   struct PostgresClosure *pg = cls;
    4296         199 :   struct GNUNET_PQ_QueryParam params[] = {
    4297         199 :     GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
    4298         199 :     GNUNET_PQ_query_param_auto_from_type (&deposit->h_contract_terms),
    4299         199 :     GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
    4300             :     GNUNET_PQ_query_param_end
    4301             :   };
    4302             :   struct TALER_EXCHANGEDB_Deposit deposit2;
    4303         199 :   struct GNUNET_PQ_ResultSpec rs[] = {
    4304         199 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    4305             :                                  &deposit2.amount_with_fee),
    4306         199 :     TALER_PQ_result_spec_absolute_time ("wallet_timestamp",
    4307             :                                         &deposit2.timestamp),
    4308         199 :     TALER_PQ_result_spec_absolute_time ("exchange_timestamp",
    4309             :                                         exchange_timestamp),
    4310         199 :     TALER_PQ_result_spec_absolute_time ("refund_deadline",
    4311             :                                         &deposit2.refund_deadline),
    4312         199 :     TALER_PQ_result_spec_absolute_time ("wire_deadline",
    4313             :                                         &deposit2.wire_deadline),
    4314         199 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    4315             :                                  deposit_fee),
    4316         199 :     GNUNET_PQ_result_spec_auto_from_type ("h_wire",
    4317             :                                           &deposit2.h_wire),
    4318             :     GNUNET_PQ_result_spec_end
    4319             :   };
    4320             :   enum GNUNET_DB_QueryStatus qs;
    4321             : #if EXPLICIT_LOCKS
    4322             :   struct GNUNET_PQ_QueryParam no_params[] = {
    4323             :     GNUNET_PQ_query_param_end
    4324             :   };
    4325             : 
    4326             :   if (0 > (qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    4327             :                                                     "lock_deposit",
    4328             :                                                     no_params)))
    4329             :     return qs;
    4330             : #endif
    4331         199 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    4332             :               "Getting deposits for coin %s\n",
    4333             :               TALER_B2S (&deposit->coin.coin_pub));
    4334         199 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    4335             :                                                  "get_deposit",
    4336             :                                                  params,
    4337             :                                                  rs);
    4338         199 :   if (0 >= qs)
    4339          87 :     return qs;
    4340             :   /* Now we check that the other information in @a deposit
    4341             :      also matches, and if not report inconsistencies. */
    4342         114 :   if ( ( (check_extras) &&
    4343           2 :          ( (0 != TALER_amount_cmp (&deposit->amount_with_fee,
    4344           2 :                                    &deposit2.amount_with_fee)) ||
    4345           2 :            (deposit->timestamp.abs_value_us !=
    4346           2 :             deposit2.timestamp.abs_value_us) ) ) ||
    4347         112 :        (deposit->refund_deadline.abs_value_us !=
    4348         112 :         deposit2.refund_deadline.abs_value_us) ||
    4349         112 :        (0 != GNUNET_memcmp (&deposit->h_wire,
    4350             :                             &deposit2.h_wire) ) )
    4351             :   {
    4352             :     /* Inconsistencies detected! Does not match!  (We might want to
    4353             :        expand the API with a 'get_deposit' function to return the
    4354             :        original transaction details to be used for an error message
    4355             :        in the future!) #3838 */
    4356           2 :     return 0;   /* Counts as if the transaction was not there */
    4357             :   }
    4358         110 :   return 1;
    4359             : }
    4360             : 
    4361             : 
    4362             : /**
    4363             :  * Mark a deposit as tiny, thereby declaring that it cannot be
    4364             :  * executed by itself and should no longer be returned by
    4365             :  * @e iterate_ready_deposits()
    4366             :  *
    4367             :  * @param cls the @e cls of this struct with the plugin-specific state
    4368             :  * @param rowid identifies the deposit row to modify
    4369             :  * @return query result status
    4370             :  */
    4371             : static enum GNUNET_DB_QueryStatus
    4372          11 : postgres_mark_deposit_tiny (void *cls,
    4373             :                             uint64_t rowid)
    4374             : {
    4375          11 :   struct PostgresClosure *pg = cls;
    4376          11 :   struct GNUNET_PQ_QueryParam params[] = {
    4377          11 :     GNUNET_PQ_query_param_uint64 (&rowid),
    4378             :     GNUNET_PQ_query_param_end
    4379             :   };
    4380             : 
    4381          11 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    4382             :                                              "mark_deposit_tiny",
    4383             :                                              params);
    4384             : }
    4385             : 
    4386             : 
    4387             : /**
    4388             :  * Test if a deposit was marked as done, thereby declaring that it cannot be
    4389             :  * refunded anymore.
    4390             :  *
    4391             :  * @param cls the @e cls of this struct with the plugin-specific state
    4392             :  * @param coin_pub the coin to check for deposit
    4393             :  * @param merchant_pub merchant to receive the deposit
    4394             :  * @param h_contract_terms contract terms of the deposit
    4395             :  * @param h_wire hash of the merchant's wire details
    4396             :  * @return #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if is is marked done,
    4397             :  *         #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if not,
    4398             :  *         otherwise transaction error status (incl. deposit unknown)
    4399             :  */
    4400             : static enum GNUNET_DB_QueryStatus
    4401           2 : postgres_test_deposit_done (void *cls,
    4402             :                             const struct TALER_CoinSpendPublicKeyP *coin_pub,
    4403             :                             const struct TALER_MerchantPublicKeyP *merchant_pub,
    4404             :                             const struct GNUNET_HashCode *h_contract_terms,
    4405             :                             const struct GNUNET_HashCode *h_wire)
    4406             : {
    4407           2 :   struct PostgresClosure *pg = cls;
    4408           2 :   struct GNUNET_PQ_QueryParam params[] = {
    4409           2 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    4410           2 :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    4411           2 :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    4412           2 :     GNUNET_PQ_query_param_auto_from_type (h_wire),
    4413             :     GNUNET_PQ_query_param_end
    4414             :   };
    4415           2 :   uint8_t done = 0;
    4416           2 :   struct GNUNET_PQ_ResultSpec rs[] = {
    4417           2 :     GNUNET_PQ_result_spec_auto_from_type ("done",
    4418             :                                           &done),
    4419             :     GNUNET_PQ_result_spec_end
    4420             :   };
    4421             :   enum GNUNET_DB_QueryStatus qs;
    4422             : 
    4423           2 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    4424             :                                                  "test_deposit_done",
    4425             :                                                  params,
    4426             :                                                  rs);
    4427           2 :   if (qs < 0)
    4428           0 :     return qs;
    4429           2 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    4430           0 :     return GNUNET_DB_STATUS_HARD_ERROR; /* deposit MUST exist */
    4431             :   return (done
    4432             :           ? GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
    4433           2 :           : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS);
    4434             : }
    4435             : 
    4436             : 
    4437             : /**
    4438             :  * Mark a deposit as done, thereby declaring that it cannot be
    4439             :  * executed at all anymore, and should no longer be returned by
    4440             :  * @e iterate_ready_deposits() or @e iterate_matching_deposits().
    4441             :  *
    4442             :  * @param cls the @e cls of this struct with the plugin-specific state
    4443             :  * @param rowid identifies the deposit row to modify
    4444             :  * @return query result status
    4445             :  */
    4446             : static enum GNUNET_DB_QueryStatus
    4447          93 : postgres_mark_deposit_done (void *cls,
    4448             :                             uint64_t rowid)
    4449             : {
    4450          93 :   struct PostgresClosure *pg = cls;
    4451          93 :   struct GNUNET_PQ_QueryParam params[] = {
    4452          93 :     GNUNET_PQ_query_param_uint64 (&rowid),
    4453             :     GNUNET_PQ_query_param_end
    4454             :   };
    4455             : 
    4456          93 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    4457             :                                              "mark_deposit_done",
    4458             :                                              params);
    4459             : }
    4460             : 
    4461             : 
    4462             : /**
    4463             :  * Obtain information about deposits that are ready to be executed.
    4464             :  * Such deposits must not be marked as "tiny" or "done", and the
    4465             :  * execution time must be in the past.
    4466             :  *
    4467             :  * @param cls the @e cls of this struct with the plugin-specific state
    4468             :  * @param deposit_cb function to call for ONE such deposit
    4469             :  * @param deposit_cb_cls closure for @a deposit_cb
    4470             :  * @return transaction status code
    4471             :  */
    4472             : static enum GNUNET_DB_QueryStatus
    4473         100 : postgres_get_ready_deposit (void *cls,
    4474             :                             TALER_EXCHANGEDB_DepositIterator deposit_cb,
    4475             :                             void *deposit_cb_cls)
    4476             : {
    4477         100 :   struct PostgresClosure *pg = cls;
    4478         100 :   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
    4479         100 :   struct GNUNET_PQ_QueryParam params[] = {
    4480         100 :     TALER_PQ_query_param_absolute_time (&now),
    4481             :     GNUNET_PQ_query_param_end
    4482             :   };
    4483             :   struct TALER_Amount amount_with_fee;
    4484             :   struct TALER_Amount deposit_fee;
    4485             :   struct GNUNET_TIME_Absolute wire_deadline;
    4486             :   struct GNUNET_TIME_Absolute wallet_timestamp;
    4487             :   struct GNUNET_TIME_Absolute exchange_timestamp;
    4488             :   struct GNUNET_HashCode h_contract_terms;
    4489             :   struct TALER_MerchantPublicKeyP merchant_pub;
    4490             :   struct TALER_CoinSpendPublicKeyP coin_pub;
    4491             :   uint64_t serial_id;
    4492             :   json_t *wire;
    4493         100 :   struct GNUNET_PQ_ResultSpec rs[] = {
    4494         100 :     GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
    4495             :                                   &serial_id),
    4496         100 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    4497             :                                  &amount_with_fee),
    4498         100 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    4499             :                                  &deposit_fee),
    4500         100 :     TALER_PQ_result_spec_absolute_time ("exchange_timestamp",
    4501             :                                         &exchange_timestamp),
    4502         100 :     TALER_PQ_result_spec_absolute_time ("wallet_timestamp",
    4503             :                                         &wallet_timestamp),
    4504         100 :     TALER_PQ_result_spec_absolute_time ("wire_deadline",
    4505             :                                         &wire_deadline),
    4506         100 :     GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    4507             :                                           &h_contract_terms),
    4508         100 :     GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    4509             :                                           &merchant_pub),
    4510         100 :     GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    4511             :                                           &coin_pub),
    4512         100 :     TALER_PQ_result_spec_json ("wire",
    4513             :                                &wire),
    4514             :     GNUNET_PQ_result_spec_end
    4515             :   };
    4516             :   enum GNUNET_DB_QueryStatus qs;
    4517             : 
    4518         100 :   (void) GNUNET_TIME_round_abs (&now);
    4519         100 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    4520             :               "Finding ready deposits by deadline %s (%llu)\n",
    4521             :               GNUNET_STRINGS_absolute_time_to_string (now),
    4522             :               (unsigned long long) now.abs_value_us);
    4523             : 
    4524         100 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    4525             :                                                  "deposits_get_ready",
    4526             :                                                  params,
    4527             :                                                  rs);
    4528         100 :   if (qs <= 0)
    4529          48 :     return qs;
    4530             : 
    4531          52 :   qs = deposit_cb (deposit_cb_cls,
    4532             :                    serial_id,
    4533             :                    exchange_timestamp,
    4534             :                    wallet_timestamp,
    4535             :                    &merchant_pub,
    4536             :                    &coin_pub,
    4537             :                    &amount_with_fee,
    4538             :                    &deposit_fee,
    4539             :                    &h_contract_terms,
    4540             :                    wire_deadline,
    4541             :                    wire);
    4542          52 :   GNUNET_PQ_cleanup_result (rs);
    4543          52 :   return qs;
    4544             : }
    4545             : 
    4546             : 
    4547             : /**
    4548             :  * Closure for #match_deposit_cb().
    4549             :  */
    4550             : struct MatchingDepositContext
    4551             : {
    4552             :   /**
    4553             :    * Function to call for each result
    4554             :    */
    4555             :   TALER_EXCHANGEDB_MatchingDepositIterator deposit_cb;
    4556             : 
    4557             :   /**
    4558             :    * Closure for @e deposit_cb.
    4559             :    */
    4560             :   void *deposit_cb_cls;
    4561             : 
    4562             :   /**
    4563             :    * Public key of the merchant against which we are matching.
    4564             :    */
    4565             :   const struct TALER_MerchantPublicKeyP *merchant_pub;
    4566             : 
    4567             :   /**
    4568             :    * Plugin context.
    4569             :    */
    4570             :   struct PostgresClosure *pg;
    4571             : 
    4572             :   /**
    4573             :    * Maximum number of results to return.
    4574             :    */
    4575             :   uint32_t limit;
    4576             : 
    4577             :   /**
    4578             :    * Loop counter, actual number of results returned.
    4579             :    */
    4580             :   unsigned int i;
    4581             : 
    4582             :   /**
    4583             :    * Set to #GNUNET_SYSERR on hard errors.
    4584             :    */
    4585             :   int status;
    4586             : };
    4587             : 
    4588             : 
    4589             : /**
    4590             :  * Helper function for #postgres_iterate_matching_deposits().
    4591             :  * To be called with the results of a SELECT statement
    4592             :  * that has returned @a num_results results.
    4593             :  *
    4594             :  * @param cls closure of type `struct MatchingDepositContext *`
    4595             :  * @param result the postgres result
    4596             :  * @param num_results the number of results in @a result
    4597             :  */
    4598             : static void
    4599          51 : match_deposit_cb (void *cls,
    4600             :                   PGresult *result,
    4601             :                   unsigned int num_results)
    4602             : {
    4603          51 :   struct MatchingDepositContext *mdc = cls;
    4604          51 :   struct PostgresClosure *pg = mdc->pg;
    4605             : 
    4606          51 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    4607             :               "Found %u/%u matching deposits\n",
    4608             :               num_results,
    4609             :               mdc->limit);
    4610          51 :   num_results = GNUNET_MIN (num_results,
    4611             :                             mdc->limit);
    4612          94 :   for (mdc->i = 0; mdc->i<num_results; mdc->i++)
    4613             :   {
    4614             :     struct TALER_Amount amount_with_fee;
    4615             :     struct TALER_Amount deposit_fee;
    4616             :     struct GNUNET_HashCode h_contract_terms;
    4617             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    4618             :     uint64_t serial_id;
    4619             :     enum GNUNET_DB_QueryStatus qs;
    4620          43 :     struct GNUNET_PQ_ResultSpec rs[] = {
    4621          43 :       GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
    4622             :                                     &serial_id),
    4623          43 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    4624             :                                    &amount_with_fee),
    4625          43 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    4626             :                                    &deposit_fee),
    4627          43 :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    4628             :                                             &h_contract_terms),
    4629          43 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    4630             :                                             &coin_pub),
    4631             :       GNUNET_PQ_result_spec_end
    4632             :     };
    4633             : 
    4634          43 :     if (GNUNET_OK !=
    4635          43 :         GNUNET_PQ_extract_result (result,
    4636             :                                   rs,
    4637          43 :                                   mdc->i))
    4638             :     {
    4639           0 :       GNUNET_break (0);
    4640           0 :       mdc->status = GNUNET_SYSERR;
    4641           0 :       return;
    4642             :     }
    4643          43 :     qs = mdc->deposit_cb (mdc->deposit_cb_cls,
    4644             :                           serial_id,
    4645             :                           &coin_pub,
    4646             :                           &amount_with_fee,
    4647             :                           &deposit_fee,
    4648             :                           &h_contract_terms);
    4649          43 :     GNUNET_PQ_cleanup_result (rs);
    4650          43 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    4651           0 :       break;
    4652             :   }
    4653             : }
    4654             : 
    4655             : 
    4656             : /**
    4657             :  * Obtain information about other pending deposits for the same
    4658             :  * destination.  Those deposits must not already be "done".
    4659             :  *
    4660             :  * @param cls the @e cls of this struct with the plugin-specific state
    4661             :  * @param h_wire destination of the wire transfer
    4662             :  * @param merchant_pub public key of the merchant
    4663             :  * @param deposit_cb function to call for each deposit
    4664             :  * @param deposit_cb_cls closure for @a deposit_cb
    4665             :  * @param limit maximum number of matching deposits to return
    4666             :  * @return transaction status code, if positive:
    4667             :  *         number of rows processed, 0 if none exist
    4668             :  */
    4669             : static enum GNUNET_DB_QueryStatus
    4670          51 : postgres_iterate_matching_deposits (
    4671             :   void *cls,
    4672             :   const struct GNUNET_HashCode *h_wire,
    4673             :   const struct TALER_MerchantPublicKeyP *merchant_pub,
    4674             :   TALER_EXCHANGEDB_MatchingDepositIterator deposit_cb,
    4675             :   void *deposit_cb_cls,
    4676             :   uint32_t limit)
    4677             : {
    4678          51 :   struct PostgresClosure *pg = cls;
    4679          51 :   struct GNUNET_PQ_QueryParam params[] = {
    4680          51 :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    4681          51 :     GNUNET_PQ_query_param_auto_from_type (h_wire),
    4682             :     GNUNET_PQ_query_param_end
    4683             :   };
    4684             :   struct MatchingDepositContext mdc;
    4685             :   enum GNUNET_DB_QueryStatus qs;
    4686             : 
    4687          51 :   mdc.deposit_cb = deposit_cb;
    4688          51 :   mdc.deposit_cb_cls = deposit_cb_cls;
    4689          51 :   mdc.merchant_pub = merchant_pub;
    4690          51 :   mdc.pg = pg;
    4691          51 :   mdc.limit = limit;
    4692          51 :   mdc.status = GNUNET_OK;
    4693          51 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    4694             :                                              "deposits_iterate_matching",
    4695             :                                              params,
    4696             :                                              &match_deposit_cb,
    4697             :                                              &mdc);
    4698          51 :   if (GNUNET_OK != mdc.status)
    4699             :   {
    4700           0 :     GNUNET_break (0);
    4701           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    4702             :   }
    4703          51 :   if (qs >= 0)
    4704          51 :     return mdc.i;
    4705           0 :   return qs;
    4706             : }
    4707             : 
    4708             : 
    4709             : /**
    4710             :  * Retrieve the record for a known coin.
    4711             :  *
    4712             :  * @param cls the plugin closure
    4713             :  * @param coin_pub the public key of the coin to search for
    4714             :  * @param coin_info place holder for the returned coin information object
    4715             :  * @return transaction status code
    4716             :  */
    4717             : static enum GNUNET_DB_QueryStatus
    4718         382 : postgres_get_known_coin (void *cls,
    4719             :                          const struct TALER_CoinSpendPublicKeyP *coin_pub,
    4720             :                          struct TALER_CoinPublicInfo *coin_info)
    4721             : {
    4722         382 :   struct PostgresClosure *pg = cls;
    4723         382 :   struct GNUNET_PQ_QueryParam params[] = {
    4724         382 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    4725             :     GNUNET_PQ_query_param_end
    4726             :   };
    4727         382 :   struct GNUNET_PQ_ResultSpec rs[] = {
    4728         382 :     GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    4729             :                                           &coin_info->denom_pub_hash),
    4730         382 :     GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    4731             :                                          &coin_info->denom_sig.rsa_signature),
    4732             :     GNUNET_PQ_result_spec_end
    4733             :   };
    4734             : 
    4735         382 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    4736             :               "Getting known coin data for coin %s\n",
    4737             :               TALER_B2S (coin_pub));
    4738         382 :   coin_info->coin_pub = *coin_pub;
    4739         382 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    4740             :                                                    "get_known_coin",
    4741             :                                                    params,
    4742             :                                                    rs);
    4743             : }
    4744             : 
    4745             : 
    4746             : /**
    4747             :  * Retrieve the denomination of a known coin.
    4748             :  *
    4749             :  * @param cls the plugin closure
    4750             :  * @param coin_pub the public key of the coin to search for
    4751             :  * @param[out] denom_hash where to store the hash of the coins denomination
    4752             :  * @return transaction status code
    4753             :  */
    4754             : static enum GNUNET_DB_QueryStatus
    4755           9 : postgres_get_coin_denomination (
    4756             :   void *cls,
    4757             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    4758             :   struct GNUNET_HashCode *denom_hash)
    4759             : {
    4760           9 :   struct PostgresClosure *pg = cls;
    4761           9 :   struct GNUNET_PQ_QueryParam params[] = {
    4762           9 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    4763             :     GNUNET_PQ_query_param_end
    4764             :   };
    4765           9 :   struct GNUNET_PQ_ResultSpec rs[] = {
    4766           9 :     GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    4767             :                                           denom_hash),
    4768             :     GNUNET_PQ_result_spec_end
    4769             :   };
    4770             : 
    4771           9 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    4772             :               "Getting coin denomination of coin %s\n",
    4773             :               TALER_B2S (coin_pub));
    4774           9 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    4775             :                                                    "get_coin_denomination",
    4776             :                                                    params,
    4777             :                                                    rs);
    4778             : }
    4779             : 
    4780             : 
    4781             : /**
    4782             :  * Insert a coin we know of into the DB.  The coin can then be
    4783             :  * referenced by tables for deposits, refresh and refund
    4784             :  * functionality.
    4785             :  *
    4786             :  * @param cls plugin closure
    4787             :  * @param coin_info the public coin info
    4788             :  * @return query result status
    4789             :  */
    4790             : static enum GNUNET_DB_QueryStatus
    4791          64 : insert_known_coin (void *cls,
    4792             :                    const struct TALER_CoinPublicInfo *coin_info)
    4793             : {
    4794          64 :   struct PostgresClosure *pg = cls;
    4795          64 :   struct GNUNET_PQ_QueryParam params[] = {
    4796          64 :     GNUNET_PQ_query_param_auto_from_type (&coin_info->coin_pub),
    4797          64 :     GNUNET_PQ_query_param_auto_from_type (&coin_info->denom_pub_hash),
    4798          64 :     GNUNET_PQ_query_param_rsa_signature (coin_info->denom_sig.rsa_signature),
    4799             :     GNUNET_PQ_query_param_end
    4800             :   };
    4801             : 
    4802          64 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    4803             :               "Creating known coin %s\n",
    4804             :               TALER_B2S (&coin_info->coin_pub));
    4805          64 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    4806             :                                              "insert_known_coin",
    4807             :                                              params);
    4808             : }
    4809             : 
    4810             : 
    4811             : /**
    4812             :  * Count the number of known coins by denomination.
    4813             :  *
    4814             :  * @param cls database connection plugin state
    4815             :  * @param denom_pub_hash denomination to count by
    4816             :  * @return number of coins if non-negative, otherwise an `enum GNUNET_DB_QueryStatus`
    4817             :  */
    4818             : static long long
    4819         289 : postgres_count_known_coins (void *cls,
    4820             :                             const struct GNUNET_HashCode *denom_pub_hash)
    4821             : {
    4822         289 :   struct PostgresClosure *pg = cls;
    4823             :   uint64_t count;
    4824         289 :   struct GNUNET_PQ_QueryParam params[] = {
    4825         289 :     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
    4826             :     GNUNET_PQ_query_param_end
    4827             :   };
    4828         289 :   struct GNUNET_PQ_ResultSpec rs[] = {
    4829         289 :     GNUNET_PQ_result_spec_uint64 ("count",
    4830             :                                   &count),
    4831             :     GNUNET_PQ_result_spec_end
    4832             :   };
    4833             :   enum GNUNET_DB_QueryStatus qs;
    4834             : 
    4835         289 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    4836             :                                                  "count_known_coins",
    4837             :                                                  params,
    4838             :                                                  rs);
    4839         289 :   if (0 > qs)
    4840           0 :     return (long long) qs;
    4841         289 :   return (long long) count;
    4842             : }
    4843             : 
    4844             : 
    4845             : /**
    4846             :  * Make sure the given @a coin is known to the database.
    4847             :  *
    4848             :  * @param cls database connection plugin state
    4849             :  * @param coin the coin that must be made known
    4850             :  * @return database transaction status, non-negative on success
    4851             :  */
    4852             : static enum TALER_EXCHANGEDB_CoinKnownStatus
    4853         103 : postgres_ensure_coin_known (void *cls,
    4854             :                             const struct TALER_CoinPublicInfo *coin)
    4855             : {
    4856         103 :   struct PostgresClosure *pg = cls;
    4857             :   enum GNUNET_DB_QueryStatus qs;
    4858             :   struct GNUNET_HashCode denom_pub_hash;
    4859         103 :   struct GNUNET_PQ_QueryParam params[] = {
    4860         103 :     GNUNET_PQ_query_param_auto_from_type (&coin->coin_pub),
    4861             :     GNUNET_PQ_query_param_end
    4862             :   };
    4863         103 :   struct GNUNET_PQ_ResultSpec rs[] = {
    4864         103 :     GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    4865             :                                           &denom_pub_hash),
    4866             :     GNUNET_PQ_result_spec_end
    4867             :   };
    4868             : #if EXPLICIT_LOCKS
    4869             :   struct GNUNET_PQ_QueryParam no_params[] = {
    4870             :     GNUNET_PQ_query_param_end
    4871             :   };
    4872             : 
    4873             :   if (0 > (qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    4874             :                                                     "lock_known_coins",
    4875             :                                                     no_params)))
    4876             :     return qs;
    4877             : #endif
    4878             :   /* check if the coin is already known */
    4879         103 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    4880             :                                                  "get_known_coin_dh",
    4881             :                                                  params,
    4882             :                                                  rs);
    4883         103 :   switch (qs)
    4884             :   {
    4885           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
    4886           0 :     return TALER_EXCHANGEDB_CKS_HARD_FAIL;
    4887           0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
    4888           0 :     return TALER_EXCHANGEDB_CKS_SOFT_FAIL;
    4889          39 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    4890          39 :     if (0 == GNUNET_memcmp (&denom_pub_hash,
    4891             :                             &coin->denom_pub_hash))
    4892          36 :       return TALER_EXCHANGEDB_CKS_PRESENT;
    4893           3 :     GNUNET_break_op (0);
    4894           3 :     return TALER_EXCHANGEDB_CKS_CONFLICT;
    4895          64 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    4896          64 :     break;
    4897             :   }
    4898             : 
    4899             :   /* if not known, insert it */
    4900          64 :   qs = insert_known_coin (pg,
    4901             :                           coin);
    4902          64 :   switch (qs)
    4903             :   {
    4904           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
    4905           0 :     return TALER_EXCHANGEDB_CKS_SOFT_FAIL;
    4906           0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
    4907           0 :     return TALER_EXCHANGEDB_CKS_HARD_FAIL;
    4908           0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    4909           0 :     GNUNET_break (0);
    4910           0 :     return TALER_EXCHANGEDB_CKS_HARD_FAIL;
    4911          64 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    4912          64 :     break;
    4913             :   }
    4914          64 :   return TALER_EXCHANGEDB_CKS_ADDED;
    4915             : }
    4916             : 
    4917             : 
    4918             : /**
    4919             :  * Insert information about deposited coin into the database.
    4920             :  *
    4921             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    4922             :  * @param exchange_timestamp time the exchange received the deposit request
    4923             :  * @param deposit deposit information to store
    4924             :  * @return query result status
    4925             :  */
    4926             : static enum GNUNET_DB_QueryStatus
    4927          57 : postgres_insert_deposit (void *cls,
    4928             :                          struct GNUNET_TIME_Absolute exchange_timestamp,
    4929             :                          const struct TALER_EXCHANGEDB_Deposit *deposit)
    4930             : {
    4931          57 :   struct PostgresClosure *pg = cls;
    4932          57 :   struct GNUNET_PQ_QueryParam params[] = {
    4933          57 :     GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
    4934          57 :     TALER_PQ_query_param_amount (&deposit->amount_with_fee),
    4935          57 :     TALER_PQ_query_param_absolute_time (&deposit->timestamp),
    4936          57 :     TALER_PQ_query_param_absolute_time (&deposit->refund_deadline),
    4937          57 :     TALER_PQ_query_param_absolute_time (&deposit->wire_deadline),
    4938          57 :     GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
    4939          57 :     GNUNET_PQ_query_param_auto_from_type (&deposit->h_contract_terms),
    4940          57 :     GNUNET_PQ_query_param_auto_from_type (&deposit->h_wire),
    4941          57 :     GNUNET_PQ_query_param_auto_from_type (&deposit->csig),
    4942          57 :     TALER_PQ_query_param_json (deposit->receiver_wire_account),
    4943          57 :     TALER_PQ_query_param_absolute_time (&exchange_timestamp),
    4944             :     GNUNET_PQ_query_param_end
    4945             :   };
    4946             : 
    4947          57 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    4948             :               "Inserting deposit to be executed at %s (%llu/%llu)\n",
    4949             :               GNUNET_STRINGS_absolute_time_to_string (deposit->wire_deadline),
    4950             :               (unsigned long long) deposit->wire_deadline.abs_value_us,
    4951             :               (unsigned long long) deposit->refund_deadline.abs_value_us);
    4952          57 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    4953             :                                              "insert_deposit",
    4954             :                                              params);
    4955             : }
    4956             : 
    4957             : 
    4958             : /**
    4959             :  * Insert information about refunded coin into the database.
    4960             :  *
    4961             :  * @param cls the @e cls of this struct with the plugin-specific state
    4962             :  * @param refund refund information to store
    4963             :  * @return query result status
    4964             :  */
    4965             : static enum GNUNET_DB_QueryStatus
    4966           5 : postgres_insert_refund (void *cls,
    4967             :                         const struct TALER_EXCHANGEDB_Refund *refund)
    4968             : {
    4969           5 :   struct PostgresClosure *pg = cls;
    4970           5 :   struct GNUNET_PQ_QueryParam params[] = {
    4971           5 :     GNUNET_PQ_query_param_auto_from_type (&refund->coin.coin_pub),
    4972           5 :     GNUNET_PQ_query_param_auto_from_type (&refund->details.merchant_pub),
    4973           5 :     GNUNET_PQ_query_param_auto_from_type (&refund->details.merchant_sig),
    4974           5 :     GNUNET_PQ_query_param_auto_from_type (&refund->details.h_contract_terms),
    4975           5 :     GNUNET_PQ_query_param_uint64 (&refund->details.rtransaction_id),
    4976           5 :     TALER_PQ_query_param_amount (&refund->details.refund_amount),
    4977             :     GNUNET_PQ_query_param_end
    4978             :   };
    4979             : 
    4980           5 :   GNUNET_assert (GNUNET_YES ==
    4981             :                  TALER_amount_cmp_currency (&refund->details.refund_amount,
    4982             :                                             &refund->details.refund_fee));
    4983           5 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    4984             :                                              "insert_refund",
    4985             :                                              params);
    4986             : }
    4987             : 
    4988             : 
    4989             : /**
    4990             :  * Closure for #get_refunds_cb().
    4991             :  */
    4992             : struct SelectRefundContext
    4993             : {
    4994             :   /**
    4995             :    * Function to call on each result.
    4996             :    */
    4997             :   TALER_EXCHANGEDB_RefundCoinCallback cb;
    4998             : 
    4999             :   /**
    5000             :    * Closure for @a cb.
    5001             :    */
    5002             :   void *cb_cls;
    5003             : 
    5004             :   /**
    5005             :    * Plugin context.
    5006             :    */
    5007             :   struct PostgresClosure *pg;
    5008             : 
    5009             :   /**
    5010             :    * Set to #GNUNET_SYSERR on error.
    5011             :    */
    5012             :   int status;
    5013             : };
    5014             : 
    5015             : 
    5016             : /**
    5017             :  * Function to be called with the results of a SELECT statement
    5018             :  * that has returned @a num_results results.
    5019             :  *
    5020             :  * @param cls closure of type `struct SelectRefundContext *`
    5021             :  * @param result the postgres result
    5022             :  * @param num_results the number of results in @a result
    5023             :  */
    5024             : static void
    5025          93 : get_refunds_cb (void *cls,
    5026             :                 PGresult *result,
    5027             :                 unsigned int num_results)
    5028             : {
    5029          93 :   struct SelectRefundContext *srctx = cls;
    5030          93 :   struct PostgresClosure *pg = srctx->pg;
    5031             : 
    5032         106 :   for (unsigned int i = 0; i<num_results; i++)
    5033             :   {
    5034             :     struct TALER_Amount amount_with_fee;
    5035          13 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5036          13 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    5037             :                                    &amount_with_fee),
    5038             :       GNUNET_PQ_result_spec_end
    5039             :     };
    5040             : 
    5041          13 :     if (GNUNET_OK !=
    5042          13 :         GNUNET_PQ_extract_result (result,
    5043             :                                   rs,
    5044             :                                   i))
    5045             :     {
    5046           0 :       GNUNET_break (0);
    5047           0 :       srctx->status = GNUNET_SYSERR;
    5048           0 :       return;
    5049             :     }
    5050          13 :     if (GNUNET_OK !=
    5051          13 :         srctx->cb (srctx->cb_cls,
    5052             :                    &amount_with_fee))
    5053           0 :       return;
    5054             :   }
    5055             : }
    5056             : 
    5057             : 
    5058             : /**
    5059             :  * Select refunds by @a coin_pub, @a merchant_pub and @a h_contract.
    5060             :  *
    5061             :  * @param cls closure of plugin
    5062             :  * @param coin_pub coin to get refunds for
    5063             :  * @param merchant_pub merchant to get refunds for
    5064             :  * @param h_contract contract (hash) to get refunds for
    5065             :  * @param cb function to call for each refund found
    5066             :  * @param cb_cls closure for @a cb
    5067             :  * @return query result status
    5068             :  */
    5069             : static enum GNUNET_DB_QueryStatus
    5070          93 : postgres_select_refunds_by_coin (
    5071             :   void *cls,
    5072             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    5073             :   const struct TALER_MerchantPublicKeyP *merchant_pub,
    5074             :   const struct GNUNET_HashCode *h_contract,
    5075             :   TALER_EXCHANGEDB_RefundCoinCallback cb,
    5076             :   void *cb_cls)
    5077             : {
    5078          93 :   struct PostgresClosure *pg = cls;
    5079             :   enum GNUNET_DB_QueryStatus qs;
    5080          93 :   struct GNUNET_PQ_QueryParam params[] = {
    5081          93 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    5082          93 :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    5083          93 :     GNUNET_PQ_query_param_auto_from_type (h_contract),
    5084             :     GNUNET_PQ_query_param_end
    5085             :   };
    5086          93 :   struct SelectRefundContext srctx = {
    5087             :     .cb = cb,
    5088             :     .cb_cls = cb_cls,
    5089             :     .pg = pg,
    5090             :     .status = GNUNET_OK
    5091             :   };
    5092             : 
    5093          93 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    5094             :                                              "get_refunds_by_coin_and_contract",
    5095             :                                              params,
    5096             :                                              &get_refunds_cb,
    5097             :                                              &srctx);
    5098          93 :   if (GNUNET_SYSERR == srctx.status)
    5099           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    5100          93 :   return qs;
    5101             : }
    5102             : 
    5103             : 
    5104             : /**
    5105             :  * Lookup refresh melt commitment data under the given @a rc.
    5106             :  *
    5107             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    5108             :  * @param rc commitment hash to use to locate the operation
    5109             :  * @param[out] melt where to store the result; note that
    5110             :  *             melt->session.coin.denom_sig will be set to NULL
    5111             :  *             and is not fetched by this routine (as it is not needed by the client)
    5112             :  * @return transaction status
    5113             :  */
    5114             : static enum GNUNET_DB_QueryStatus
    5115          17 : postgres_get_melt (void *cls,
    5116             :                    const struct TALER_RefreshCommitmentP *rc,
    5117             :                    struct TALER_EXCHANGEDB_Melt *melt)
    5118             : {
    5119          17 :   struct PostgresClosure *pg = cls;
    5120          17 :   struct GNUNET_PQ_QueryParam params[] = {
    5121          17 :     GNUNET_PQ_query_param_auto_from_type (rc),
    5122             :     GNUNET_PQ_query_param_end
    5123             :   };
    5124          17 :   struct GNUNET_PQ_ResultSpec rs[] = {
    5125          17 :     GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    5126             :                                           &melt->session.coin.
    5127             :                                           denom_pub_hash),
    5128          17 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
    5129             :                                  &melt->melt_fee),
    5130          17 :     GNUNET_PQ_result_spec_uint32 ("noreveal_index",
    5131             :                                   &melt->session.noreveal_index),
    5132          17 :     GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
    5133             :                                           &melt->session.coin.coin_pub),
    5134          17 :     GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
    5135             :                                           &melt->session.coin_sig),
    5136          17 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    5137             :                                  &melt->session.amount_with_fee),
    5138             :     GNUNET_PQ_result_spec_end
    5139             :   };
    5140             :   enum GNUNET_DB_QueryStatus qs;
    5141             : 
    5142          17 :   melt->session.coin.denom_sig.rsa_signature = NULL;
    5143          17 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    5144             :                                                  "get_melt",
    5145             :                                                  params,
    5146             :                                                  rs);
    5147          17 :   melt->session.rc = *rc;
    5148          17 :   return qs;
    5149             : }
    5150             : 
    5151             : 
    5152             : /**
    5153             :  * Lookup noreveal index of a previous melt operation under the given
    5154             :  * @a rc.
    5155             :  *
    5156             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    5157             :  * @param rc commitment hash to use to locate the operation
    5158             :  * @param[out] noreveal_index returns the "gamma" value selected by the
    5159             :  *             exchange which is the index of the transfer key that is
    5160             :  *             not to be revealed to the exchange
    5161             :  * @return transaction status
    5162             :  */
    5163             : static enum GNUNET_DB_QueryStatus
    5164          20 : postgres_get_melt_index (void *cls,
    5165             :                          const struct TALER_RefreshCommitmentP *rc,
    5166             :                          uint32_t *noreveal_index)
    5167             : {
    5168          20 :   struct PostgresClosure *pg = cls;
    5169          20 :   struct GNUNET_PQ_QueryParam params[] = {
    5170          20 :     GNUNET_PQ_query_param_auto_from_type (rc),
    5171             :     GNUNET_PQ_query_param_end
    5172             :   };
    5173          20 :   struct GNUNET_PQ_ResultSpec rs[] = {
    5174          20 :     GNUNET_PQ_result_spec_uint32 ("noreveal_index",
    5175             :                                   noreveal_index),
    5176             :     GNUNET_PQ_result_spec_end
    5177             :   };
    5178             : 
    5179          20 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    5180             :                                                    "get_melt_index",
    5181             :                                                    params,
    5182             :                                                    rs);
    5183             : }
    5184             : 
    5185             : 
    5186             : /**
    5187             :  * Store new refresh melt commitment data.
    5188             :  *
    5189             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    5190             :  * @param refresh_session session data to store
    5191             :  * @return query status for the transaction
    5192             :  */
    5193             : static enum GNUNET_DB_QueryStatus
    5194          16 : postgres_insert_melt (
    5195             :   void *cls,
    5196             :   const struct TALER_EXCHANGEDB_Refresh *refresh_session)
    5197             : {
    5198          16 :   struct PostgresClosure *pg = cls;
    5199          16 :   struct GNUNET_PQ_QueryParam params[] = {
    5200          16 :     GNUNET_PQ_query_param_auto_from_type (&refresh_session->rc),
    5201          16 :     GNUNET_PQ_query_param_auto_from_type (&refresh_session->coin.coin_pub),
    5202          16 :     GNUNET_PQ_query_param_auto_from_type (&refresh_session->coin_sig),
    5203          16 :     TALER_PQ_query_param_amount (&refresh_session->amount_with_fee),
    5204          16 :     GNUNET_PQ_query_param_uint32 (&refresh_session->noreveal_index),
    5205             :     GNUNET_PQ_query_param_end
    5206             :   };
    5207             : 
    5208          16 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    5209             :                                              "insert_melt",
    5210             :                                              params);
    5211             : }
    5212             : 
    5213             : 
    5214             : /**
    5215             :  * Store in the database which coin(s) the wallet wanted to create
    5216             :  * in a given refresh operation and all of the other information
    5217             :  * we learned or created in the /refresh/reveal step.
    5218             :  *
    5219             :  * @param cls the @e cls of this struct with the plugin-specific state
    5220             :  * @param rc identify commitment and thus refresh operation
    5221             :  * @param num_rrcs number of coins to generate, size of the @a rrcs array
    5222             :  * @param rrcs information about the new coins
    5223             :  * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1
    5224             :  * @param tprivs transfer private keys to store
    5225             :  * @param tp public key to store
    5226             :  * @return query status for the transaction
    5227             :  */
    5228             : static enum GNUNET_DB_QueryStatus
    5229           7 : postgres_insert_refresh_reveal (
    5230             :   void *cls,
    5231             :   const struct TALER_RefreshCommitmentP *rc,
    5232             :   uint32_t num_rrcs,
    5233             :   const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
    5234             :   unsigned int num_tprivs,
    5235             :   const struct TALER_TransferPrivateKeyP *tprivs,
    5236             :   const struct TALER_TransferPublicKeyP *tp)
    5237             : {
    5238           7 :   struct PostgresClosure *pg = cls;
    5239             : 
    5240           7 :   if (TALER_CNC_KAPPA != num_tprivs + 1)
    5241             :   {
    5242           0 :     GNUNET_break (0);
    5243           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    5244             :   }
    5245          27 :   for (uint32_t i = 0; i<num_rrcs; i++)
    5246             :   {
    5247          20 :     const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
    5248             :     struct GNUNET_HashCode denom_pub_hash;
    5249             :     struct GNUNET_HashCode h_coin_ev;
    5250          20 :     struct GNUNET_PQ_QueryParam params[] = {
    5251          20 :       GNUNET_PQ_query_param_auto_from_type (rc),
    5252          20 :       GNUNET_PQ_query_param_uint32 (&i),
    5253          20 :       GNUNET_PQ_query_param_auto_from_type (&rrc->orig_coin_link_sig),
    5254          20 :       GNUNET_PQ_query_param_auto_from_type (&denom_pub_hash),
    5255          20 :       GNUNET_PQ_query_param_fixed_size (rrc->coin_ev,
    5256             :                                         rrc->coin_ev_size),
    5257          20 :       GNUNET_PQ_query_param_auto_from_type (&h_coin_ev),
    5258          20 :       GNUNET_PQ_query_param_rsa_signature (rrc->coin_sig.rsa_signature),
    5259             :       GNUNET_PQ_query_param_end
    5260             :     };
    5261             :     enum GNUNET_DB_QueryStatus qs;
    5262             : 
    5263          20 :     GNUNET_CRYPTO_rsa_public_key_hash (rrc->denom_pub.rsa_public_key,
    5264             :                                        &denom_pub_hash);
    5265          20 :     GNUNET_CRYPTO_hash (rrc->coin_ev,
    5266             :                         rrc->coin_ev_size,
    5267             :                         &h_coin_ev);
    5268          20 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    5269             :                                              "insert_refresh_revealed_coin",
    5270             :                                              params);
    5271          20 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    5272           0 :       return qs;
    5273             :   }
    5274             : 
    5275             :   {
    5276           7 :     struct GNUNET_PQ_QueryParam params[] = {
    5277           7 :       GNUNET_PQ_query_param_auto_from_type (rc),
    5278           7 :       GNUNET_PQ_query_param_auto_from_type (tp),
    5279           7 :       GNUNET_PQ_query_param_fixed_size (tprivs,
    5280             :                                         num_tprivs
    5281             :                                         * sizeof (struct
    5282             :                                                   TALER_TransferPrivateKeyP)),
    5283             :       GNUNET_PQ_query_param_end
    5284             :     };
    5285             : 
    5286           7 :     return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    5287             :                                                "insert_refresh_transfer_keys",
    5288             :                                                params);
    5289             :   }
    5290             : }
    5291             : 
    5292             : 
    5293             : /**
    5294             :  * Context where we aggregate data from the database.
    5295             :  * Closure for #add_revealed_coins().
    5296             :  */
    5297             : struct GetRevealContext
    5298             : {
    5299             :   /**
    5300             :    * Array of revealed coins we obtained from the DB.
    5301             :    */
    5302             :   struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs;
    5303             : 
    5304             :   /**
    5305             :    * Length of the @a rrcs array.
    5306             :    */
    5307             :   unsigned int rrcs_len;
    5308             : 
    5309             :   /**
    5310             :    * Set to an error code if we ran into trouble.
    5311             :    */
    5312             :   enum GNUNET_DB_QueryStatus qs;
    5313             : };
    5314             : 
    5315             : 
    5316             : /**
    5317             :  * Function to be called with the results of a SELECT statement
    5318             :  * that has returned @a num_results results.
    5319             :  *
    5320             :  * @param cls closure of type `struct GetRevealContext`
    5321             :  * @param result the postgres result
    5322             :  * @param num_results the number of results in @a result
    5323             :  */
    5324             : static void
    5325         161 : add_revealed_coins (void *cls,
    5326             :                     PGresult *result,
    5327             :                     unsigned int num_results)
    5328             : {
    5329         161 :   struct GetRevealContext *grctx = cls;
    5330             : 
    5331         161 :   if (0 == num_results)
    5332          12 :     return;
    5333         149 :   grctx->rrcs = GNUNET_new_array (num_results,
    5334             :                                   struct TALER_EXCHANGEDB_RefreshRevealedCoin);
    5335         149 :   grctx->rrcs_len = num_results;
    5336        2044 :   for (unsigned int i = 0; i < num_results; i++)
    5337             :   {
    5338        1895 :     struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &grctx->rrcs[i];
    5339             :     uint32_t off;
    5340        1895 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5341        1895 :       GNUNET_PQ_result_spec_uint32 ("freshcoin_index",
    5342             :                                     &off),
    5343        1895 :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    5344             :                                             &rrc->denom_pub.rsa_public_key),
    5345        1895 :       GNUNET_PQ_result_spec_auto_from_type ("link_sig",
    5346             :                                             &rrc->orig_coin_link_sig),
    5347        1895 :       GNUNET_PQ_result_spec_variable_size ("coin_ev",
    5348        1895 :                                            (void **) &rrc->coin_ev,
    5349             :                                            &rrc->coin_ev_size),
    5350        1895 :       GNUNET_PQ_result_spec_rsa_signature ("ev_sig",
    5351             :                                            &rrc->coin_sig.rsa_signature),
    5352             :       GNUNET_PQ_result_spec_end
    5353             :     };
    5354             : 
    5355        1895 :     if (GNUNET_OK !=
    5356        1895 :         GNUNET_PQ_extract_result (result,
    5357             :                                   rs,
    5358             :                                   i))
    5359             :     {
    5360           0 :       GNUNET_break (0);
    5361           0 :       grctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    5362           0 :       return;
    5363             :     }
    5364        1895 :     if (off != i)
    5365             :     {
    5366           0 :       GNUNET_break (0);
    5367           0 :       grctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    5368           0 :       return;
    5369             :     }
    5370             :   }
    5371             : }
    5372             : 
    5373             : 
    5374             : /**
    5375             :  * Lookup in the database the coins that we want to
    5376             :  * create in the given refresh operation.
    5377             :  *
    5378             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    5379             :  * @param rc identify commitment and thus refresh operation
    5380             :  * @param cb function to call with the results
    5381             :  * @param cb_cls closure for @a cb
    5382             :  * @return transaction status
    5383             :  */
    5384             : static enum GNUNET_DB_QueryStatus
    5385         161 : postgres_get_refresh_reveal (void *cls,
    5386             :                              const struct TALER_RefreshCommitmentP *rc,
    5387             :                              TALER_EXCHANGEDB_RefreshCallback cb,
    5388             :                              void *cb_cls)
    5389             : {
    5390         161 :   struct PostgresClosure *pg = cls;
    5391             :   struct GetRevealContext grctx;
    5392             :   enum GNUNET_DB_QueryStatus qs;
    5393             :   struct TALER_TransferPublicKeyP tp;
    5394             :   void *tpriv;
    5395             :   size_t tpriv_size;
    5396         161 :   struct GNUNET_PQ_QueryParam params[] = {
    5397         161 :     GNUNET_PQ_query_param_auto_from_type (rc),
    5398             :     GNUNET_PQ_query_param_end
    5399             :   };
    5400         161 :   struct GNUNET_PQ_ResultSpec rs[] = {
    5401         161 :     GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
    5402             :                                           &tp),
    5403         161 :     GNUNET_PQ_result_spec_variable_size ("transfer_privs",
    5404             :                                          &tpriv,
    5405             :                                          &tpriv_size),
    5406             :     GNUNET_PQ_result_spec_end
    5407             :   };
    5408             : 
    5409             :   /* First get the coins */
    5410         161 :   memset (&grctx,
    5411             :           0,
    5412             :           sizeof (grctx));
    5413         161 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    5414             :                                              "get_refresh_revealed_coins",
    5415             :                                              params,
    5416             :                                              &add_revealed_coins,
    5417             :                                              &grctx);
    5418         161 :   switch (qs)
    5419             :   {
    5420          12 :   case GNUNET_DB_STATUS_HARD_ERROR:
    5421             :   case GNUNET_DB_STATUS_SOFT_ERROR:
    5422             :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    5423          12 :     goto cleanup;
    5424         149 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    5425             :   default:   /* can have more than one result */
    5426         149 :     break;
    5427             :   }
    5428         149 :   switch (grctx.qs)
    5429             :   {
    5430           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
    5431             :   case GNUNET_DB_STATUS_SOFT_ERROR:
    5432           0 :     goto cleanup;
    5433         149 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    5434             :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:   /* should be impossible */
    5435         149 :     break;
    5436             :   }
    5437             : 
    5438             :   /* now also get the transfer keys (public and private) */
    5439         149 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    5440             :                                                  "get_refresh_transfer_keys",
    5441             :                                                  params,
    5442             :                                                  rs);
    5443         149 :   switch (qs)
    5444             :   {
    5445           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
    5446             :   case GNUNET_DB_STATUS_SOFT_ERROR:
    5447             :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    5448           0 :     goto cleanup;
    5449         149 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    5450         149 :     break;
    5451           0 :   default:
    5452           0 :     GNUNET_assert (0);
    5453             :   }
    5454         149 :   if ( (0 != tpriv_size % sizeof (struct TALER_TransferPrivateKeyP)) ||
    5455         149 :        (TALER_CNC_KAPPA - 1 != tpriv_size / sizeof (struct
    5456             :                                                     TALER_TransferPrivateKeyP)) )
    5457             :   {
    5458           0 :     GNUNET_break (0);
    5459           0 :     qs = GNUNET_DB_STATUS_HARD_ERROR;
    5460           0 :     GNUNET_PQ_cleanup_result (rs);
    5461           0 :     goto cleanup;
    5462             :   }
    5463             : 
    5464             :   /* Pass result back to application */
    5465         149 :   cb (cb_cls,
    5466             :       grctx.rrcs_len,
    5467         149 :       grctx.rrcs,
    5468         149 :       tpriv_size / sizeof (struct TALER_TransferPrivateKeyP),
    5469             :       (const struct TALER_TransferPrivateKeyP *) tpriv,
    5470             :       &tp);
    5471         149 :   GNUNET_PQ_cleanup_result (rs);
    5472             : 
    5473         161 : cleanup:
    5474        2056 :   for (unsigned int i = 0; i < grctx.rrcs_len; i++)
    5475             :   {
    5476        1895 :     struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &grctx.rrcs[i];
    5477             : 
    5478        1895 :     if (NULL != rrc->denom_pub.rsa_public_key)
    5479        1895 :       GNUNET_CRYPTO_rsa_public_key_free (rrc->denom_pub.rsa_public_key);
    5480        1895 :     if (NULL != rrc->coin_sig.rsa_signature)
    5481        1895 :       GNUNET_CRYPTO_rsa_signature_free (rrc->coin_sig.rsa_signature);
    5482        1895 :     GNUNET_free (rrc->coin_ev);
    5483             :   }
    5484         161 :   GNUNET_free (grctx.rrcs);
    5485             : 
    5486         161 :   return qs;
    5487             : }
    5488             : 
    5489             : 
    5490             : /**
    5491             :  * Closure for #add_ldl().
    5492             :  */
    5493             : struct LinkDataContext
    5494             : {
    5495             :   /**
    5496             :    * Function to call on each result.
    5497             :    */
    5498             :   TALER_EXCHANGEDB_LinkCallback ldc;
    5499             : 
    5500             :   /**
    5501             :    * Closure for @e ldc.
    5502             :    */
    5503             :   void *ldc_cls;
    5504             : 
    5505             :   /**
    5506             :    * Last transfer public key for which we have information in @e last.
    5507             :    * Only valid if @e last is non-NULL.
    5508             :    */
    5509             :   struct TALER_TransferPublicKeyP transfer_pub;
    5510             : 
    5511             :   /**
    5512             :    * Link data for @e transfer_pub
    5513             :    */
    5514             :   struct TALER_EXCHANGEDB_LinkList *last;
    5515             : 
    5516             :   /**
    5517             :    * Status, set to #GNUNET_SYSERR on errors,
    5518             :    */
    5519             :   int status;
    5520             : };
    5521             : 
    5522             : 
    5523             : /**
    5524             :  * Free memory of the link data list.
    5525             :  *
    5526             :  * @param cls the @e cls of this struct with the plugin-specific state (unused)
    5527             :  * @param ldl link data list to release
    5528             :  */
    5529             : static void
    5530           2 : free_link_data_list (void *cls,
    5531             :                      struct TALER_EXCHANGEDB_LinkList *ldl)
    5532             : {
    5533             :   struct TALER_EXCHANGEDB_LinkList *next;
    5534             : 
    5535          11 :   while (NULL != ldl)
    5536             :   {
    5537           9 :     next = ldl->next;
    5538           9 :     if (NULL != ldl->denom_pub.rsa_public_key)
    5539           9 :       GNUNET_CRYPTO_rsa_public_key_free (ldl->denom_pub.rsa_public_key);
    5540           9 :     if (NULL != ldl->ev_sig.rsa_signature)
    5541           9 :       GNUNET_CRYPTO_rsa_signature_free (ldl->ev_sig.rsa_signature);
    5542           9 :     GNUNET_free (ldl);
    5543           9 :     ldl = next;
    5544             :   }
    5545           2 : }
    5546             : 
    5547             : 
    5548             : /**
    5549             :  * Function to be called with the results of a SELECT statement
    5550             :  * that has returned @a num_results results.
    5551             :  *
    5552             :  * @param cls closure of type `struct LinkDataContext *`
    5553             :  * @param result the postgres result
    5554             :  * @param num_results the number of results in @a result
    5555             :  */
    5556             : static void
    5557           2 : add_ldl (void *cls,
    5558             :          PGresult *result,
    5559             :          unsigned int num_results)
    5560             : {
    5561           2 :   struct LinkDataContext *ldctx = cls;
    5562             : 
    5563          11 :   for (int i = num_results - 1; i >= 0; i--)
    5564             :   {
    5565             :     struct TALER_EXCHANGEDB_LinkList *pos;
    5566             :     struct TALER_TransferPublicKeyP transfer_pub;
    5567             : 
    5568           9 :     pos = GNUNET_new (struct TALER_EXCHANGEDB_LinkList);
    5569             :     {
    5570           9 :       struct GNUNET_PQ_ResultSpec rs[] = {
    5571           9 :         GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
    5572             :                                               &transfer_pub),
    5573           9 :         GNUNET_PQ_result_spec_auto_from_type ("link_sig",
    5574             :                                               &pos->orig_coin_link_sig),
    5575           9 :         GNUNET_PQ_result_spec_rsa_signature ("ev_sig",
    5576             :                                              &pos->ev_sig.rsa_signature),
    5577           9 :         GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    5578             :                                               &pos->denom_pub.rsa_public_key),
    5579             :         GNUNET_PQ_result_spec_end
    5580             :       };
    5581             : 
    5582           9 :       if (GNUNET_OK !=
    5583           9 :           GNUNET_PQ_extract_result (result,
    5584             :                                     rs,
    5585             :                                     i))
    5586             :       {
    5587           0 :         GNUNET_break (0);
    5588           0 :         GNUNET_free (pos);
    5589           0 :         ldctx->status = GNUNET_SYSERR;
    5590           0 :         return;
    5591             :       }
    5592             :     }
    5593           9 :     if ( (NULL != ldctx->last) &&
    5594           7 :          (0 == GNUNET_memcmp (&transfer_pub,
    5595             :                               &ldctx->transfer_pub)) )
    5596             :     {
    5597           7 :       pos->next = ldctx->last;
    5598             :     }
    5599             :     else
    5600             :     {
    5601           2 :       if (NULL != ldctx->last)
    5602             :       {
    5603           0 :         ldctx->ldc (ldctx->ldc_cls,
    5604           0 :                     &ldctx->transfer_pub,
    5605           0 :                     ldctx->last);
    5606           0 :         free_link_data_list (cls,
    5607             :                              ldctx->last);
    5608             :       }
    5609           2 :       ldctx->transfer_pub = transfer_pub;
    5610             :     }
    5611           9 :     ldctx->last = pos;
    5612             :   }
    5613             : }
    5614             : 
    5615             : 
    5616             : /**
    5617             :  * Obtain the link data of a coin, that is the encrypted link
    5618             :  * information, the denomination keys and the signatures.
    5619             :  *
    5620             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    5621             :  * @param coin_pub public key of the coin
    5622             :  * @param ldc function to call for each session the coin was melted into
    5623             :  * @param ldc_cls closure for @a tdc
    5624             :  * @return transaction status code
    5625             :  */
    5626             : static enum GNUNET_DB_QueryStatus
    5627           2 : postgres_get_link_data (void *cls,
    5628             :                         const struct TALER_CoinSpendPublicKeyP *coin_pub,
    5629             :                         TALER_EXCHANGEDB_LinkCallback ldc,
    5630             :                         void *ldc_cls)
    5631             : {
    5632           2 :   struct PostgresClosure *pg = cls;
    5633           2 :   struct GNUNET_PQ_QueryParam params[] = {
    5634           2 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    5635             :     GNUNET_PQ_query_param_end
    5636             :   };
    5637             :   enum GNUNET_DB_QueryStatus qs;
    5638             :   struct LinkDataContext ldctx;
    5639             : 
    5640           2 :   ldctx.ldc = ldc;
    5641           2 :   ldctx.ldc_cls = ldc_cls;
    5642           2 :   ldctx.last = NULL;
    5643           2 :   ldctx.status = GNUNET_OK;
    5644           2 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    5645             :                                              "get_link",
    5646             :                                              params,
    5647             :                                              &add_ldl,
    5648             :                                              &ldctx);
    5649           2 :   if (NULL != ldctx.last)
    5650             :   {
    5651           2 :     if (GNUNET_OK == ldctx.status)
    5652             :     {
    5653             :       /* call callback one more time! */
    5654           2 :       ldc (ldc_cls,
    5655             :            &ldctx.transfer_pub,
    5656           2 :            ldctx.last);
    5657             :     }
    5658           2 :     free_link_data_list (cls,
    5659             :                          ldctx.last);
    5660           2 :     ldctx.last = NULL;
    5661             :   }
    5662           2 :   if (GNUNET_OK != ldctx.status)
    5663           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    5664           2 :   return qs;
    5665             : }
    5666             : 
    5667             : 
    5668             : /**
    5669             :  * Closure for callbacks called from #postgres_get_coin_transactions()
    5670             :  */
    5671             : struct CoinHistoryContext
    5672             : {
    5673             :   /**
    5674             :    * Head of the coin's history list.
    5675             :    */
    5676             :   struct TALER_EXCHANGEDB_TransactionList *head;
    5677             : 
    5678             :   /**
    5679             :    * Public key of the coin we are building the history for.
    5680             :    */
    5681             :   const struct TALER_CoinSpendPublicKeyP *coin_pub;
    5682             : 
    5683             :   /**
    5684             :    * Closure for all callbacks of this database plugin.
    5685             :    */
    5686             :   void *db_cls;
    5687             : 
    5688             :   /**
    5689             :    * Plugin context.
    5690             :    */
    5691             :   struct PostgresClosure *pg;
    5692             : 
    5693             :   /**
    5694             :    * Set to 'true' if the transaction failed.
    5695             :    */
    5696             :   bool failed;
    5697             : 
    5698             :   /**
    5699             :    * Set to 'true' if we found a deposit or melt (for invariant check).
    5700             :    */
    5701             :   bool have_deposit_or_melt;
    5702             : };
    5703             : 
    5704             : 
    5705             : /**
    5706             :  * Function to be called with the results of a SELECT statement
    5707             :  * that has returned @a num_results results.
    5708             :  *
    5709             :  * @param cls closure of type `struct CoinHistoryContext`
    5710             :  * @param result the postgres result
    5711             :  * @param num_results the number of results in @a result
    5712             :  */
    5713             : static void
    5714         322 : add_coin_deposit (void *cls,
    5715             :                   PGresult *result,
    5716             :                   unsigned int num_results)
    5717             : {
    5718         322 :   struct CoinHistoryContext *chc = cls;
    5719         322 :   struct PostgresClosure *pg = chc->pg;
    5720             : 
    5721         541 :   for (unsigned int i = 0; i < num_results; i++)
    5722             :   {
    5723             :     struct TALER_EXCHANGEDB_DepositListEntry *deposit;
    5724             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    5725             :     uint64_t serial_id;
    5726             : 
    5727         219 :     chc->have_deposit_or_melt = true;
    5728         219 :     deposit = GNUNET_new (struct TALER_EXCHANGEDB_DepositListEntry);
    5729             :     {
    5730         219 :       uint8_t done = 0;
    5731         219 :       struct GNUNET_PQ_ResultSpec rs[] = {
    5732         219 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    5733             :                                      &deposit->amount_with_fee),
    5734         219 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    5735             :                                      &deposit->deposit_fee),
    5736         219 :         TALER_PQ_result_spec_absolute_time ("wallet_timestamp",
    5737             :                                             &deposit->timestamp),
    5738         219 :         TALER_PQ_result_spec_absolute_time ("refund_deadline",
    5739             :                                             &deposit->refund_deadline),
    5740         219 :         TALER_PQ_result_spec_absolute_time ("wire_deadline",
    5741             :                                             &deposit->wire_deadline),
    5742         219 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    5743             :                                               &deposit->h_denom_pub),
    5744         219 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    5745             :                                               &deposit->merchant_pub),
    5746         219 :         GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    5747             :                                               &deposit->h_contract_terms),
    5748         219 :         GNUNET_PQ_result_spec_auto_from_type ("h_wire",
    5749             :                                               &deposit->h_wire),
    5750         219 :         TALER_PQ_result_spec_json ("wire",
    5751             :                                    &deposit->receiver_wire_account),
    5752         219 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    5753             :                                               &deposit->csig),
    5754         219 :         GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
    5755             :                                       &serial_id),
    5756         219 :         GNUNET_PQ_result_spec_auto_from_type ("done",
    5757             :                                               &done),
    5758             :         GNUNET_PQ_result_spec_end
    5759             :       };
    5760             : 
    5761         219 :       if (GNUNET_OK !=
    5762         219 :           GNUNET_PQ_extract_result (result,
    5763             :                                     rs,
    5764             :                                     i))
    5765             :       {
    5766           0 :         GNUNET_break (0);
    5767           0 :         GNUNET_free (deposit);
    5768           0 :         chc->failed = true;
    5769           0 :         return;
    5770             :       }
    5771         219 :       deposit->done = (0 != done);
    5772             :     }
    5773         219 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    5774         219 :     tl->next = chc->head;
    5775         219 :     tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
    5776         219 :     tl->details.deposit = deposit;
    5777         219 :     tl->serial_id = serial_id;
    5778         219 :     chc->head = tl;
    5779             :   }
    5780             : }
    5781             : 
    5782             : 
    5783             : /**
    5784             :  * Function to be called with the results of a SELECT statement
    5785             :  * that has returned @a num_results results.
    5786             :  *
    5787             :  * @param cls closure of type `struct CoinHistoryContext`
    5788             :  * @param result the postgres result
    5789             :  * @param num_results the number of results in @a result
    5790             :  */
    5791             : static void
    5792         322 : add_coin_melt (void *cls,
    5793             :                PGresult *result,
    5794             :                unsigned int num_results)
    5795             : {
    5796         322 :   struct CoinHistoryContext *chc = cls;
    5797         322 :   struct PostgresClosure *pg = chc->pg;
    5798             : 
    5799         594 :   for (unsigned int i = 0; i<num_results; i++)
    5800             :   {
    5801             :     struct TALER_EXCHANGEDB_MeltListEntry *melt;
    5802             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    5803             :     uint64_t serial_id;
    5804             : 
    5805         272 :     chc->have_deposit_or_melt = true;
    5806         272 :     melt = GNUNET_new (struct TALER_EXCHANGEDB_MeltListEntry);
    5807             :     {
    5808         272 :       struct GNUNET_PQ_ResultSpec rs[] = {
    5809         272 :         GNUNET_PQ_result_spec_auto_from_type ("rc",
    5810             :                                               &melt->rc),
    5811             :         /* oldcoin_index not needed */
    5812         272 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    5813             :                                               &melt->h_denom_pub),
    5814         272 :         GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
    5815             :                                               &melt->coin_sig),
    5816         272 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    5817             :                                      &melt->amount_with_fee),
    5818         272 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
    5819             :                                      &melt->melt_fee),
    5820         272 :         GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
    5821             :                                       &serial_id),
    5822             :         GNUNET_PQ_result_spec_end
    5823             :       };
    5824             : 
    5825         272 :       if (GNUNET_OK !=
    5826         272 :           GNUNET_PQ_extract_result (result,
    5827             :                                     rs,
    5828             :                                     i))
    5829             :       {
    5830           0 :         GNUNET_break (0);
    5831           0 :         GNUNET_free (melt);
    5832           0 :         chc->failed = true;
    5833           0 :         return;
    5834             :       }
    5835             :     }
    5836         272 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    5837         272 :     tl->next = chc->head;
    5838         272 :     tl->type = TALER_EXCHANGEDB_TT_MELT;
    5839         272 :     tl->details.melt = melt;
    5840         272 :     tl->serial_id = serial_id;
    5841         272 :     chc->head = tl;
    5842             :   }
    5843             : }
    5844             : 
    5845             : 
    5846             : /**
    5847             :  * Function to be called with the results of a SELECT statement
    5848             :  * that has returned @a num_results results.
    5849             :  *
    5850             :  * @param cls closure of type `struct CoinHistoryContext`
    5851             :  * @param result the postgres result
    5852             :  * @param num_results the number of results in @a result
    5853             :  */
    5854             : static void
    5855         322 : add_coin_refund (void *cls,
    5856             :                  PGresult *result,
    5857             :                  unsigned int num_results)
    5858             : {
    5859         322 :   struct CoinHistoryContext *chc = cls;
    5860         322 :   struct PostgresClosure *pg = chc->pg;
    5861             : 
    5862         381 :   for (unsigned int i = 0; i<num_results; i++)
    5863             :   {
    5864             :     struct TALER_EXCHANGEDB_RefundListEntry *refund;
    5865             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    5866             :     uint64_t serial_id;
    5867             : 
    5868          59 :     refund = GNUNET_new (struct TALER_EXCHANGEDB_RefundListEntry);
    5869             :     {
    5870          59 :       struct GNUNET_PQ_ResultSpec rs[] = {
    5871          59 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    5872             :                                               &refund->merchant_pub),
    5873          59 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
    5874             :                                               &refund->merchant_sig),
    5875          59 :         GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    5876             :                                               &refund->h_contract_terms),
    5877          59 :         GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
    5878             :                                       &refund->rtransaction_id),
    5879          59 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    5880             :                                      &refund->refund_amount),
    5881          59 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
    5882             :                                      &refund->refund_fee),
    5883          59 :         GNUNET_PQ_result_spec_uint64 ("refund_serial_id",
    5884             :                                       &serial_id),
    5885             :         GNUNET_PQ_result_spec_end
    5886             :       };
    5887             : 
    5888          59 :       if (GNUNET_OK !=
    5889          59 :           GNUNET_PQ_extract_result (result,
    5890             :                                     rs,
    5891             :                                     i))
    5892             :       {
    5893           0 :         GNUNET_break (0);
    5894           0 :         GNUNET_free (refund);
    5895           0 :         chc->failed = true;
    5896           0 :         return;
    5897             :       }
    5898             :     }
    5899          59 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    5900          59 :     tl->next = chc->head;
    5901          59 :     tl->type = TALER_EXCHANGEDB_TT_REFUND;
    5902          59 :     tl->details.refund = refund;
    5903          59 :     tl->serial_id = serial_id;
    5904          59 :     chc->head = tl;
    5905             :   }
    5906             : }
    5907             : 
    5908             : 
    5909             : /**
    5910             :  * Function to be called with the results of a SELECT statement
    5911             :  * that has returned @a num_results results.
    5912             :  *
    5913             :  * @param cls closure of type `struct CoinHistoryContext`
    5914             :  * @param result the postgres result
    5915             :  * @param num_results the number of results in @a result
    5916             :  */
    5917             : static void
    5918         273 : add_old_coin_recoup (void *cls,
    5919             :                      PGresult *result,
    5920             :                      unsigned int num_results)
    5921             : {
    5922         273 :   struct CoinHistoryContext *chc = cls;
    5923         273 :   struct PostgresClosure *pg = chc->pg;
    5924             : 
    5925         330 :   for (unsigned int i = 0; i<num_results; i++)
    5926             :   {
    5927             :     struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
    5928             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    5929             :     uint64_t serial_id;
    5930             : 
    5931          57 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
    5932             :     {
    5933          57 :       struct GNUNET_PQ_ResultSpec rs[] = {
    5934          57 :         GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    5935             :                                               &recoup->coin.coin_pub),
    5936          57 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    5937             :                                               &recoup->coin_sig),
    5938          57 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
    5939             :                                               &recoup->coin_blind),
    5940          57 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    5941             :                                      &recoup->value),
    5942          57 :         TALER_PQ_result_spec_absolute_time ("timestamp",
    5943             :                                             &recoup->timestamp),
    5944          57 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    5945             :                                               &recoup->coin.denom_pub_hash),
    5946          57 :         GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    5947             :                                              &recoup->coin.denom_sig.
    5948             :                                              rsa_signature),
    5949          57 :         GNUNET_PQ_result_spec_uint64 ("recoup_refresh_uuid",
    5950             :                                       &serial_id),
    5951             :         GNUNET_PQ_result_spec_end
    5952             :       };
    5953             : 
    5954          57 :       if (GNUNET_OK !=
    5955          57 :           GNUNET_PQ_extract_result (result,
    5956             :                                     rs,
    5957             :                                     i))
    5958             :       {
    5959           0 :         GNUNET_break (0);
    5960           0 :         GNUNET_free (recoup);
    5961           0 :         chc->failed = true;
    5962           0 :         return;
    5963             :       }
    5964          57 :       recoup->old_coin_pub = *chc->coin_pub;
    5965             :     }
    5966          57 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    5967          57 :     tl->next = chc->head;
    5968          57 :     tl->type = TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP;
    5969          57 :     tl->details.old_coin_recoup = recoup;
    5970          57 :     tl->serial_id = serial_id;
    5971          57 :     chc->head = tl;
    5972             :   }
    5973             : }
    5974             : 
    5975             : 
    5976             : /**
    5977             :  * Function to be called with the results of a SELECT statement
    5978             :  * that has returned @a num_results results.
    5979             :  *
    5980             :  * @param cls closure of type `struct CoinHistoryContext`
    5981             :  * @param result the postgres result
    5982             :  * @param num_results the number of results in @a result
    5983             :  */
    5984             : static void
    5985         273 : add_coin_recoup (void *cls,
    5986             :                  PGresult *result,
    5987             :                  unsigned int num_results)
    5988             : {
    5989         273 :   struct CoinHistoryContext *chc = cls;
    5990         273 :   struct PostgresClosure *pg = chc->pg;
    5991             : 
    5992         282 :   for (unsigned int i = 0; i<num_results; i++)
    5993             :   {
    5994             :     struct TALER_EXCHANGEDB_RecoupListEntry *recoup;
    5995             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    5996             :     uint64_t serial_id;
    5997             : 
    5998           9 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupListEntry);
    5999             :     {
    6000           9 :       struct GNUNET_PQ_ResultSpec rs[] = {
    6001           9 :         GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    6002             :                                               &recoup->reserve_pub),
    6003           9 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    6004             :                                               &recoup->coin_sig),
    6005           9 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    6006             :                                               &recoup->h_denom_pub),
    6007           9 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
    6008             :                                               &recoup->coin_blind),
    6009           9 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    6010             :                                      &recoup->value),
    6011           9 :         TALER_PQ_result_spec_absolute_time ("timestamp",
    6012             :                                             &recoup->timestamp),
    6013           9 :         GNUNET_PQ_result_spec_uint64 ("recoup_uuid",
    6014             :                                       &serial_id),
    6015             :         GNUNET_PQ_result_spec_end
    6016             :       };
    6017             : 
    6018           9 :       if (GNUNET_OK !=
    6019           9 :           GNUNET_PQ_extract_result (result,
    6020             :                                     rs,
    6021             :                                     i))
    6022             :       {
    6023           0 :         GNUNET_break (0);
    6024           0 :         GNUNET_free (recoup);
    6025           0 :         chc->failed = true;
    6026           0 :         return;
    6027             :       }
    6028             :     }
    6029           9 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    6030           9 :     tl->next = chc->head;
    6031           9 :     tl->type = TALER_EXCHANGEDB_TT_RECOUP;
    6032           9 :     tl->details.recoup = recoup;
    6033           9 :     tl->serial_id = serial_id;
    6034           9 :     chc->head = tl;
    6035             :   }
    6036             : }
    6037             : 
    6038             : 
    6039             : /**
    6040             :  * Function to be called with the results of a SELECT statement
    6041             :  * that has returned @a num_results results.
    6042             :  *
    6043             :  * @param cls closure of type `struct CoinHistoryContext`
    6044             :  * @param result the postgres result
    6045             :  * @param num_results the number of results in @a result
    6046             :  */
    6047             : static void
    6048         273 : add_coin_recoup_refresh (void *cls,
    6049             :                          PGresult *result,
    6050             :                          unsigned int num_results)
    6051             : {
    6052         273 :   struct CoinHistoryContext *chc = cls;
    6053         273 :   struct PostgresClosure *pg = chc->pg;
    6054             : 
    6055         317 :   for (unsigned int i = 0; i<num_results; i++)
    6056             :   {
    6057             :     struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
    6058             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    6059             :     uint64_t serial_id;
    6060             : 
    6061          44 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
    6062             :     {
    6063          44 :       struct GNUNET_PQ_ResultSpec rs[] = {
    6064          44 :         GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
    6065             :                                               &recoup->old_coin_pub),
    6066          44 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    6067             :                                               &recoup->coin_sig),
    6068          44 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
    6069             :                                               &recoup->coin_blind),
    6070          44 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    6071             :                                      &recoup->value),
    6072          44 :         TALER_PQ_result_spec_absolute_time ("timestamp",
    6073             :                                             &recoup->timestamp),
    6074          44 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    6075             :                                               &recoup->coin.denom_pub_hash),
    6076          44 :         GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    6077             :                                              &recoup->coin.denom_sig.
    6078             :                                              rsa_signature),
    6079          44 :         GNUNET_PQ_result_spec_uint64 ("recoup_refresh_uuid",
    6080             :                                       &serial_id),
    6081             :         GNUNET_PQ_result_spec_end
    6082             :       };
    6083             : 
    6084          44 :       if (GNUNET_OK !=
    6085          44 :           GNUNET_PQ_extract_result (result,
    6086             :                                     rs,
    6087             :                                     i))
    6088             :       {
    6089           0 :         GNUNET_break (0);
    6090           0 :         GNUNET_free (recoup);
    6091           0 :         chc->failed = true;
    6092           0 :         return;
    6093             :       }
    6094          44 :       recoup->coin.coin_pub = *chc->coin_pub;
    6095             :     }
    6096          44 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    6097          44 :     tl->next = chc->head;
    6098          44 :     tl->type = TALER_EXCHANGEDB_TT_RECOUP_REFRESH;
    6099          44 :     tl->details.recoup_refresh = recoup;
    6100          44 :     tl->serial_id = serial_id;
    6101          44 :     chc->head = tl;
    6102             :   }
    6103             : }
    6104             : 
    6105             : 
    6106             : /**
    6107             :  * Work we need to do.
    6108             :  */
    6109             : struct Work
    6110             : {
    6111             :   /**
    6112             :    * SQL prepared statement name.
    6113             :    */
    6114             :   const char *statement;
    6115             : 
    6116             :   /**
    6117             :    * Function to call to handle the result(s).
    6118             :    */
    6119             :   GNUNET_PQ_PostgresResultHandler cb;
    6120             : };
    6121             : 
    6122             : 
    6123             : /**
    6124             :  * Compile a list of all (historic) transactions performed with the given coin
    6125             :  * (/refresh/melt, /deposit, /refund and /recoup operations).
    6126             :  *
    6127             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    6128             :  * @param coin_pub coin to investigate
    6129             :  * @param include_recoup should recoup transactions be included in the @a tlp
    6130             :  * @param[out] tlp set to list of transactions, NULL if coin is fresh
    6131             :  * @return database transaction status
    6132             :  */
    6133             : static enum GNUNET_DB_QueryStatus
    6134         322 : postgres_get_coin_transactions (
    6135             :   void *cls,
    6136             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    6137             :   int include_recoup,
    6138             :   struct TALER_EXCHANGEDB_TransactionList **tlp)
    6139             : {
    6140         322 :   struct PostgresClosure *pg = cls;
    6141             :   static const struct Work work_op[] = {
    6142             :     /** #TALER_EXCHANGEDB_TT_DEPOSIT */
    6143             :     { "get_deposit_with_coin_pub",
    6144             :       &add_coin_deposit },
    6145             :     /** #TALER_EXCHANGEDB_TT_MELT */
    6146             :     { "get_refresh_session_by_coin",
    6147             :       &add_coin_melt },
    6148             :     /** #TALER_EXCHANGEDB_TT_REFUND */
    6149             :     { "get_refunds_by_coin",
    6150             :       &add_coin_refund },
    6151             :     { NULL, NULL }
    6152             :   };
    6153             :   static const struct Work work_wp[] = {
    6154             :     /** #TALER_EXCHANGEDB_TT_DEPOSIT */
    6155             :     { "get_deposit_with_coin_pub",
    6156             :       &add_coin_deposit },
    6157             :     /** #TALER_EXCHANGEDB_TT_MELT */
    6158             :     { "get_refresh_session_by_coin",
    6159             :       &add_coin_melt },
    6160             :     /** #TALER_EXCHANGEDB_TT_REFUND */
    6161             :     { "get_refunds_by_coin",
    6162             :       &add_coin_refund },
    6163             :     /** #TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP */
    6164             :     { "recoup_by_old_coin",
    6165             :       &add_old_coin_recoup },
    6166             :     /** #TALER_EXCHANGEDB_TT_RECOUP */
    6167             :     { "recoup_by_coin",
    6168             :       &add_coin_recoup },
    6169             :     /** #TALER_EXCHANGEDB_TT_RECOUP_REFRESH */
    6170             :     { "recoup_by_refreshed_coin",
    6171             :       &add_coin_recoup_refresh },
    6172             :     { NULL, NULL }
    6173             :   };
    6174         322 :   struct GNUNET_PQ_QueryParam params[] = {
    6175         322 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    6176             :     GNUNET_PQ_query_param_end
    6177             :   };
    6178             :   enum GNUNET_DB_QueryStatus qs;
    6179             :   const struct Work *work;
    6180         322 :   struct CoinHistoryContext chc = {
    6181             :     .head = NULL,
    6182             :     .coin_pub = coin_pub,
    6183             :     .pg = pg,
    6184             :     .db_cls = cls
    6185             :   };
    6186             : 
    6187         322 :   work = (GNUNET_YES == include_recoup) ? work_wp : work_op;
    6188         322 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    6189             :               "Getting transactions for coin %s\n",
    6190             :               TALER_B2S (coin_pub));
    6191        2107 :   for (unsigned int i = 0; NULL != work[i].statement; i++)
    6192             :   {
    6193        1785 :     qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    6194        1785 :                                                work[i].statement,
    6195             :                                                params,
    6196        1785 :                                                work[i].cb,
    6197             :                                                &chc);
    6198        1785 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    6199             :                 "Coin %s yielded %d transactions of type %s\n",
    6200             :                 TALER_B2S (coin_pub),
    6201             :                 qs,
    6202             :                 work[i].statement);
    6203        1785 :     if ( (0 > qs) ||
    6204        1785 :          (chc.failed) )
    6205             :     {
    6206           0 :       if (NULL != chc.head)
    6207           0 :         common_free_coin_transaction_list (cls,
    6208             :                                            chc.head);
    6209           0 :       *tlp = NULL;
    6210           0 :       if (chc.failed)
    6211           0 :         qs = GNUNET_DB_STATUS_HARD_ERROR;
    6212           0 :       return qs;
    6213             :     }
    6214             :   }
    6215         322 :   *tlp = chc.head;
    6216         322 :   if (NULL == chc.head)
    6217          37 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    6218         285 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    6219             : }
    6220             : 
    6221             : 
    6222             : /**
    6223             :  * Closure for #handle_wt_result.
    6224             :  */
    6225             : struct WireTransferResultContext
    6226             : {
    6227             :   /**
    6228             :    * Function to call on each result.
    6229             :    */
    6230             :   TALER_EXCHANGEDB_AggregationDataCallback cb;
    6231             : 
    6232             :   /**
    6233             :    * Closure for @e cb.
    6234             :    */
    6235             :   void *cb_cls;
    6236             : 
    6237             :   /**
    6238             :    * Plugin context.
    6239             :    */
    6240             :   struct PostgresClosure *pg;
    6241             : 
    6242             :   /**
    6243             :    * Set to #GNUNET_SYSERR on serious errors.
    6244             :    */
    6245             :   int status;
    6246             : };
    6247             : 
    6248             : 
    6249             : /**
    6250             :  * Function to be called with the results of a SELECT statement
    6251             :  * that has returned @a num_results results.  Helper function
    6252             :  * for #postgres_lookup_wire_transfer().
    6253             :  *
    6254             :  * @param cls closure of type `struct WireTransferResultContext *`
    6255             :  * @param result the postgres result
    6256             :  * @param num_results the number of results in @a result
    6257             :  */
    6258             : static void
    6259          35 : handle_wt_result (void *cls,
    6260             :                   PGresult *result,
    6261             :                   unsigned int num_results)
    6262             : {
    6263          35 :   struct WireTransferResultContext *ctx = cls;
    6264          35 :   struct PostgresClosure *pg = ctx->pg;
    6265             : 
    6266          98 :   for (unsigned int i = 0; i<num_results; i++)
    6267             :   {
    6268             :     uint64_t rowid;
    6269             :     struct GNUNET_HashCode h_contract_terms;
    6270             :     struct GNUNET_HashCode h_wire;
    6271             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    6272             :     struct TALER_MerchantPublicKeyP merchant_pub;
    6273             :     struct GNUNET_TIME_Absolute exec_time;
    6274             :     struct TALER_Amount amount_with_fee;
    6275             :     struct TALER_Amount deposit_fee;
    6276             :     struct TALER_DenominationPublicKey denom_pub;
    6277             :     json_t *wire;
    6278          63 :     struct GNUNET_PQ_ResultSpec rs[] = {
    6279          63 :       GNUNET_PQ_result_spec_uint64 ("aggregation_serial_id", &rowid),
    6280          63 :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    6281             :                                             &h_contract_terms),
    6282          63 :       TALER_PQ_result_spec_json ("wire", &wire),
    6283          63 :       GNUNET_PQ_result_spec_auto_from_type ("h_wire", &h_wire),
    6284          63 :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    6285             :                                             &denom_pub.rsa_public_key),
    6286          63 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub", &coin_pub),
    6287          63 :       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub", &merchant_pub),
    6288          63 :       TALER_PQ_result_spec_absolute_time ("execution_date", &exec_time),
    6289          63 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", &amount_with_fee),
    6290          63 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit", &deposit_fee),
    6291             :       GNUNET_PQ_result_spec_end
    6292             :     };
    6293             : 
    6294          63 :     if (GNUNET_OK !=
    6295          63 :         GNUNET_PQ_extract_result (result,
    6296             :                                   rs,
    6297             :                                   i))
    6298             :     {
    6299           0 :       GNUNET_break (0);
    6300           0 :       ctx->status = GNUNET_SYSERR;
    6301           0 :       return;
    6302             :     }
    6303          63 :     ctx->cb (ctx->cb_cls,
    6304             :              rowid,
    6305             :              &merchant_pub,
    6306             :              &h_wire,
    6307             :              wire,
    6308             :              exec_time,
    6309             :              &h_contract_terms,
    6310             :              &denom_pub,
    6311             :              &coin_pub,
    6312             :              &amount_with_fee,
    6313             :              &deposit_fee);
    6314          63 :     GNUNET_PQ_cleanup_result (rs);
    6315             :   }
    6316             : }
    6317             : 
    6318             : 
    6319             : /**
    6320             :  * Lookup the list of Taler transactions that were aggregated
    6321             :  * into a wire transfer by the respective @a wtid.
    6322             :  *
    6323             :  * @param cls closure
    6324             :  * @param wtid the raw wire transfer identifier we used
    6325             :  * @param cb function to call on each transaction found
    6326             :  * @param cb_cls closure for @a cb
    6327             :  * @return query status of the transaction
    6328             :  */
    6329             : static enum GNUNET_DB_QueryStatus
    6330          35 : postgres_lookup_wire_transfer (
    6331             :   void *cls,
    6332             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    6333             :   TALER_EXCHANGEDB_AggregationDataCallback cb,
    6334             :   void *cb_cls)
    6335             : {
    6336          35 :   struct PostgresClosure *pg = cls;
    6337          35 :   struct GNUNET_PQ_QueryParam params[] = {
    6338          35 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    6339             :     GNUNET_PQ_query_param_end
    6340             :   };
    6341             :   struct WireTransferResultContext ctx;
    6342             :   enum GNUNET_DB_QueryStatus qs;
    6343             : 
    6344          35 :   ctx.cb = cb;
    6345          35 :   ctx.cb_cls = cb_cls;
    6346          35 :   ctx.pg = pg;
    6347          35 :   ctx.status = GNUNET_OK;
    6348             :   /* check if the melt record exists and get it */
    6349          35 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    6350             :                                              "lookup_transactions",
    6351             :                                              params,
    6352             :                                              &handle_wt_result,
    6353             :                                              &ctx);
    6354          35 :   if (GNUNET_OK != ctx.status)
    6355           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    6356          35 :   return qs;
    6357             : }
    6358             : 
    6359             : 
    6360             : /**
    6361             :  * Try to find the wire transfer details for a deposit operation.
    6362             :  * If we did not execute the deposit yet, return when it is supposed
    6363             :  * to be executed.
    6364             :  *
    6365             :  * @param cls closure
    6366             :  * @param h_contract_terms hash of the proposal data
    6367             :  * @param h_wire hash of merchant wire details
    6368             :  * @param coin_pub public key of deposited coin
    6369             :  * @param merchant_pub merchant public key
    6370             :  * @param cb function to call with the result
    6371             :  * @param cb_cls closure to pass to @a cb
    6372             :  * @return transaction status code
    6373             :  - */
    6374             : static enum GNUNET_DB_QueryStatus
    6375           5 : postgres_lookup_transfer_by_deposit (
    6376             :   void *cls,
    6377             :   const struct GNUNET_HashCode *h_contract_terms,
    6378             :   const struct GNUNET_HashCode *h_wire,
    6379             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    6380             :   const struct TALER_MerchantPublicKeyP *merchant_pub,
    6381             :   TALER_EXCHANGEDB_WireTransferByCoinCallback cb,
    6382             :   void *cb_cls)
    6383             : {
    6384           5 :   struct PostgresClosure *pg = cls;
    6385             :   enum GNUNET_DB_QueryStatus qs;
    6386           5 :   struct GNUNET_PQ_QueryParam params[] = {
    6387           5 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    6388           5 :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    6389           5 :     GNUNET_PQ_query_param_auto_from_type (h_wire),
    6390           5 :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    6391             :     GNUNET_PQ_query_param_end
    6392             :   };
    6393             :   struct TALER_WireTransferIdentifierRawP wtid;
    6394             :   struct GNUNET_TIME_Absolute exec_time;
    6395             :   struct TALER_Amount amount_with_fee;
    6396             :   struct TALER_Amount deposit_fee;
    6397           5 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6398           5 :     GNUNET_PQ_result_spec_auto_from_type ("wtid_raw",
    6399             :                                           &wtid),
    6400           5 :     TALER_PQ_result_spec_absolute_time ("execution_date",
    6401             :                                         &exec_time),
    6402           5 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    6403             :                                  &amount_with_fee),
    6404           5 :     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
    6405             :                                  &deposit_fee),
    6406             :     GNUNET_PQ_result_spec_end
    6407             :   };
    6408             : 
    6409             :   /* check if the melt record exists and get it */
    6410           5 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6411             :                                                  "lookup_deposit_wtid",
    6412             :                                                  params,
    6413             :                                                  rs);
    6414           5 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    6415             :   {
    6416           2 :     cb (cb_cls,
    6417             :         &wtid,
    6418             :         &amount_with_fee,
    6419             :         &deposit_fee,
    6420             :         exec_time);
    6421           2 :     return qs;
    6422             :   }
    6423           3 :   if (0 > qs)
    6424           0 :     return qs;
    6425             : 
    6426           3 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
    6427           3 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    6428             :               "lookup_deposit_wtid returned 0 matching rows\n");
    6429             :   {
    6430             :     /* Check if transaction exists in deposits, so that we just
    6431             :        do not have a WTID yet, if so, do call the CB with a NULL wtid
    6432             :        and return #GNUNET_YES! */
    6433           3 :     struct GNUNET_PQ_QueryParam params2[] = {
    6434           3 :       GNUNET_PQ_query_param_auto_from_type (coin_pub),
    6435           3 :       GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    6436           3 :       GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    6437           3 :       GNUNET_PQ_query_param_auto_from_type (h_wire),
    6438             :       GNUNET_PQ_query_param_end
    6439             :     };
    6440             :     struct GNUNET_TIME_Absolute exec_time;
    6441             :     struct TALER_Amount amount_with_fee;
    6442             :     struct TALER_Amount deposit_fee;
    6443           3 :     struct GNUNET_PQ_ResultSpec rs2[] = {
    6444           3 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", &amount_with_fee),
    6445           3 :       TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit", &deposit_fee),
    6446           3 :       TALER_PQ_result_spec_absolute_time ("wire_deadline", &exec_time),
    6447             :       GNUNET_PQ_result_spec_end
    6448             :     };
    6449             : 
    6450           3 :     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6451             :                                                    "get_deposit_for_wtid",
    6452             :                                                    params2,
    6453             :                                                    rs2);
    6454           3 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    6455             :     {
    6456             :       /* Ok, we're aware of the transaction, but it has not yet been
    6457             :          executed */
    6458           1 :       cb (cb_cls,
    6459             :           NULL,
    6460             :           &amount_with_fee,
    6461             :           &deposit_fee,
    6462             :           exec_time);
    6463           1 :       return qs;
    6464             :     }
    6465           2 :     return qs;
    6466             :   }
    6467             : }
    6468             : 
    6469             : 
    6470             : /**
    6471             :  * Function called to insert aggregation information into the DB.
    6472             :  *
    6473             :  * @param cls closure
    6474             :  * @param wtid the raw wire transfer identifier we used
    6475             :  * @param deposit_serial_id row in the deposits table for which this is aggregation data
    6476             :  * @return transaction status code
    6477             :  */
    6478             : static enum GNUNET_DB_QueryStatus
    6479          93 : postgres_insert_aggregation_tracking (
    6480             :   void *cls,
    6481             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    6482             :   unsigned long long deposit_serial_id)
    6483             : {
    6484          93 :   struct PostgresClosure *pg = cls;
    6485          93 :   uint64_t rid = deposit_serial_id;
    6486          93 :   struct GNUNET_PQ_QueryParam params[] = {
    6487          93 :     GNUNET_PQ_query_param_uint64 (&rid),
    6488          93 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    6489             :     GNUNET_PQ_query_param_end
    6490             :   };
    6491             : 
    6492          93 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    6493             :                                              "insert_aggregation_tracking",
    6494             :                                              params);
    6495             : }
    6496             : 
    6497             : 
    6498             : /**
    6499             :  * Obtain wire fee from database.
    6500             :  *
    6501             :  * @param cls closure
    6502             :  * @param type type of wire transfer the fee applies for
    6503             :  * @param date for which date do we want the fee?
    6504             :  * @param[out] start_date when does the fee go into effect
    6505             :  * @param[out] end_date when does the fee end being valid
    6506             :  * @param[out] wire_fee how high is the wire transfer fee
    6507             :  * @param[out] closing_fee how high is the closing fee
    6508             :  * @param[out] master_sig signature over the above by the exchange master key
    6509             :  * @return status of the transaction
    6510             :  */
    6511             : static enum GNUNET_DB_QueryStatus
    6512         214 : postgres_get_wire_fee (void *cls,
    6513             :                        const char *type,
    6514             :                        struct GNUNET_TIME_Absolute date,
    6515             :                        struct GNUNET_TIME_Absolute *start_date,
    6516             :                        struct GNUNET_TIME_Absolute *end_date,
    6517             :                        struct TALER_Amount *wire_fee,
    6518             :                        struct TALER_Amount *closing_fee,
    6519             :                        struct TALER_MasterSignatureP *master_sig)
    6520             : {
    6521         214 :   struct PostgresClosure *pg = cls;
    6522         214 :   struct GNUNET_PQ_QueryParam params[] = {
    6523         214 :     GNUNET_PQ_query_param_string (type),
    6524         214 :     TALER_PQ_query_param_absolute_time (&date),
    6525             :     GNUNET_PQ_query_param_end
    6526             :   };
    6527         214 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6528         214 :     TALER_PQ_result_spec_absolute_time ("start_date", start_date),
    6529         214 :     TALER_PQ_result_spec_absolute_time ("end_date", end_date),
    6530         214 :     TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee", wire_fee),
    6531         214 :     TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee", closing_fee),
    6532         214 :     GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig),
    6533             :     GNUNET_PQ_result_spec_end
    6534             :   };
    6535             : 
    6536         214 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6537             :                                                    "get_wire_fee",
    6538             :                                                    params,
    6539             :                                                    rs);
    6540             : }
    6541             : 
    6542             : 
    6543             : /**
    6544             :  * Insert wire transfer fee into database.
    6545             :  *
    6546             :  * @param cls closure
    6547             :  * @param type type of wire transfer this fee applies for
    6548             :  * @param start_date when does the fee go into effect
    6549             :  * @param end_date when does the fee end being valid
    6550             :  * @param wire_fee how high is the wire transfer fee
    6551             :  * @param closing_fee how high is the closing fee
    6552             :  * @param master_sig signature over the above by the exchange master key
    6553             :  * @return transaction status code
    6554             :  */
    6555             : static enum GNUNET_DB_QueryStatus
    6556           9 : postgres_insert_wire_fee (void *cls,
    6557             :                           const char *type,
    6558             :                           struct GNUNET_TIME_Absolute start_date,
    6559             :                           struct GNUNET_TIME_Absolute end_date,
    6560             :                           const struct TALER_Amount *wire_fee,
    6561             :                           const struct TALER_Amount *closing_fee,
    6562             :                           const struct TALER_MasterSignatureP *master_sig)
    6563             : {
    6564           9 :   struct PostgresClosure *pg = cls;
    6565           9 :   struct GNUNET_PQ_QueryParam params[] = {
    6566           9 :     GNUNET_PQ_query_param_string (type),
    6567           9 :     TALER_PQ_query_param_absolute_time (&start_date),
    6568           9 :     TALER_PQ_query_param_absolute_time (&end_date),
    6569           9 :     TALER_PQ_query_param_amount (wire_fee),
    6570           9 :     TALER_PQ_query_param_amount (closing_fee),
    6571           9 :     GNUNET_PQ_query_param_auto_from_type (master_sig),
    6572             :     GNUNET_PQ_query_param_end
    6573             :   };
    6574             :   struct TALER_Amount wf;
    6575             :   struct TALER_Amount cf;
    6576             :   struct TALER_MasterSignatureP sig;
    6577             :   struct GNUNET_TIME_Absolute sd;
    6578             :   struct GNUNET_TIME_Absolute ed;
    6579             :   enum GNUNET_DB_QueryStatus qs;
    6580             : 
    6581           9 :   qs = postgres_get_wire_fee (pg,
    6582             :                               type,
    6583             :                               start_date,
    6584             :                               &sd,
    6585             :                               &ed,
    6586             :                               &wf,
    6587             :                               &cf,
    6588             :                               &sig);
    6589           9 :   if (qs < 0)
    6590           0 :     return qs;
    6591           9 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    6592             :   {
    6593           1 :     if (0 != GNUNET_memcmp (&sig,
    6594             :                             master_sig))
    6595             :     {
    6596           0 :       GNUNET_break (0);
    6597           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    6598             :     }
    6599           1 :     if (0 != TALER_amount_cmp (wire_fee,
    6600             :                                &wf))
    6601             :     {
    6602           0 :       GNUNET_break (0);
    6603           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    6604             :     }
    6605           1 :     if (0 != TALER_amount_cmp (closing_fee,
    6606             :                                &cf))
    6607             :     {
    6608           0 :       GNUNET_break (0);
    6609           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    6610             :     }
    6611           1 :     if ( (sd.abs_value_us != start_date.abs_value_us) ||
    6612           1 :          (ed.abs_value_us != end_date.abs_value_us) )
    6613             :     {
    6614           0 :       GNUNET_break (0);
    6615           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    6616             :     }
    6617             :     /* equal record already exists */
    6618           1 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    6619             :   }
    6620             : 
    6621           8 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    6622             :                                              "insert_wire_fee",
    6623             :                                              params);
    6624             : }
    6625             : 
    6626             : 
    6627             : /**
    6628             :  * Closure for #reserve_expired_cb().
    6629             :  */
    6630             : struct ExpiredReserveContext
    6631             : {
    6632             :   /**
    6633             :    * Function to call for each expired reserve.
    6634             :    */
    6635             :   TALER_EXCHANGEDB_ReserveExpiredCallback rec;
    6636             : 
    6637             :   /**
    6638             :    * Closure to give to @e rec.
    6639             :    */
    6640             :   void *rec_cls;
    6641             : 
    6642             :   /**
    6643             :    * Plugin context.
    6644             :    */
    6645             :   struct PostgresClosure *pg;
    6646             : 
    6647             :   /**
    6648             :    * Set to #GNUNET_SYSERR on error.
    6649             :    */
    6650             :   int status;
    6651             : };
    6652             : 
    6653             : 
    6654             : /**
    6655             :  * Function to be called with the results of a SELECT statement
    6656             :  * that has returned @a num_results results.
    6657             :  *
    6658             :  * @param cls closure
    6659             :  * @param result the postgres result
    6660             :  * @param num_results the number of results in @a result
    6661             :  */
    6662             : static void
    6663          49 : reserve_expired_cb (void *cls,
    6664             :                     PGresult *result,
    6665             :                     unsigned int num_results)
    6666             : {
    6667          49 :   struct ExpiredReserveContext *erc = cls;
    6668          49 :   struct PostgresClosure *pg = erc->pg;
    6669             :   int ret;
    6670             : 
    6671          49 :   ret = GNUNET_OK;
    6672          78 :   for (unsigned int i = 0; i<num_results; i++)
    6673             :   {
    6674             :     struct GNUNET_TIME_Absolute exp_date;
    6675             :     char *account_details;
    6676             :     struct TALER_ReservePublicKeyP reserve_pub;
    6677             :     struct TALER_Amount remaining_balance;
    6678          29 :     struct GNUNET_PQ_ResultSpec rs[] = {
    6679          29 :       TALER_PQ_result_spec_absolute_time ("expiration_date",
    6680             :                                           &exp_date),
    6681          29 :       GNUNET_PQ_result_spec_string ("account_details",
    6682             :                                     &account_details),
    6683          29 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    6684             :                                             &reserve_pub),
    6685          29 :       TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
    6686             :                                    &remaining_balance),
    6687             :       GNUNET_PQ_result_spec_end
    6688             :     };
    6689             : 
    6690          29 :     if (GNUNET_OK !=
    6691          29 :         GNUNET_PQ_extract_result (result,
    6692             :                                   rs,
    6693             :                                   i))
    6694             :     {
    6695           0 :       GNUNET_break (0);
    6696           0 :       ret = GNUNET_SYSERR;
    6697           0 :       break;
    6698             :     }
    6699          29 :     ret = erc->rec (erc->rec_cls,
    6700             :                     &reserve_pub,
    6701             :                     &remaining_balance,
    6702             :                     account_details,
    6703             :                     exp_date);
    6704          29 :     GNUNET_PQ_cleanup_result (rs);
    6705          29 :     if (GNUNET_OK != ret)
    6706           0 :       break;
    6707             :   }
    6708          49 :   erc->status = ret;
    6709          49 : }
    6710             : 
    6711             : 
    6712             : /**
    6713             :  * Obtain information about expired reserves and their
    6714             :  * remaining balances.
    6715             :  *
    6716             :  * @param cls closure of the plugin
    6717             :  * @param now timestamp based on which we decide expiration
    6718             :  * @param rec function to call on expired reserves
    6719             :  * @param rec_cls closure for @a rec
    6720             :  * @return transaction status
    6721             :  */
    6722             : static enum GNUNET_DB_QueryStatus
    6723          49 : postgres_get_expired_reserves (void *cls,
    6724             :                                struct GNUNET_TIME_Absolute now,
    6725             :                                TALER_EXCHANGEDB_ReserveExpiredCallback rec,
    6726             :                                void *rec_cls)
    6727             : {
    6728          49 :   struct PostgresClosure *pg = cls;
    6729          49 :   struct GNUNET_PQ_QueryParam params[] = {
    6730          49 :     TALER_PQ_query_param_absolute_time (&now),
    6731             :     GNUNET_PQ_query_param_end
    6732             :   };
    6733             :   struct ExpiredReserveContext ectx;
    6734             :   enum GNUNET_DB_QueryStatus qs;
    6735             : 
    6736          49 :   ectx.rec = rec;
    6737          49 :   ectx.rec_cls = rec_cls;
    6738          49 :   ectx.pg = pg;
    6739          49 :   ectx.status = GNUNET_OK;
    6740          49 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    6741             :                                              "get_expired_reserves",
    6742             :                                              params,
    6743             :                                              &reserve_expired_cb,
    6744             :                                              &ectx);
    6745          49 :   if (GNUNET_OK != ectx.status)
    6746           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    6747          49 :   return qs;
    6748             : }
    6749             : 
    6750             : 
    6751             : /**
    6752             :  * Insert reserve close operation into database.
    6753             :  *
    6754             :  * @param cls closure
    6755             :  * @param reserve_pub which reserve is this about?
    6756             :  * @param execution_date when did we perform the transfer?
    6757             :  * @param receiver_account to which account do we transfer?
    6758             :  * @param wtid wire transfer details
    6759             :  * @param amount_with_fee amount we charged to the reserve
    6760             :  * @param closing_fee how high is the closing fee
    6761             :  * @return transaction status code
    6762             :  */
    6763             : static enum GNUNET_DB_QueryStatus
    6764          30 : postgres_insert_reserve_closed (
    6765             :   void *cls,
    6766             :   const struct TALER_ReservePublicKeyP *reserve_pub,
    6767             :   struct GNUNET_TIME_Absolute execution_date,
    6768             :   const char *receiver_account,
    6769             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    6770             :   const struct TALER_Amount *amount_with_fee,
    6771             :   const struct TALER_Amount *closing_fee)
    6772             : {
    6773          30 :   struct PostgresClosure *pg = cls;
    6774             :   struct TALER_EXCHANGEDB_Reserve reserve;
    6775          30 :   struct GNUNET_PQ_QueryParam params[] = {
    6776          30 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    6777          30 :     TALER_PQ_query_param_absolute_time (&execution_date),
    6778          30 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    6779          30 :     GNUNET_PQ_query_param_string (receiver_account),
    6780          30 :     TALER_PQ_query_param_amount (amount_with_fee),
    6781          30 :     TALER_PQ_query_param_amount (closing_fee),
    6782             :     GNUNET_PQ_query_param_end
    6783             :   };
    6784             :   enum TALER_AmountArithmeticResult ret;
    6785             :   enum GNUNET_DB_QueryStatus qs;
    6786             : 
    6787          30 :   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    6788             :                                            "reserves_close_insert",
    6789             :                                            params);
    6790          30 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    6791           0 :     return qs;
    6792             : 
    6793             :   /* update reserve balance */
    6794          30 :   reserve.pub = *reserve_pub;
    6795          30 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    6796          30 :       (qs = postgres_reserves_get (cls,
    6797             :                                    &reserve)))
    6798             :   {
    6799             :     /* Existence should have been checked before we got here... */
    6800           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    6801           0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    6802           0 :       qs = GNUNET_DB_STATUS_HARD_ERROR;
    6803           0 :     return qs;
    6804             :   }
    6805          30 :   ret = TALER_amount_subtract (&reserve.balance,
    6806             :                                &reserve.balance,
    6807             :                                amount_with_fee);
    6808          30 :   if (ret < 0)
    6809             :   {
    6810             :     /* The reserve history was checked to make sure there is enough of a balance
    6811             :        left before we tried this; however, concurrent operations may have changed
    6812             :        the situation by now.  We should re-try the transaction.  */
    6813           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    6814             :                 "Closing of reserve `%s' refused due to balance mismatch. Retrying.\n",
    6815             :                 TALER_B2S (reserve_pub));
    6816           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    6817             :   }
    6818          30 :   GNUNET_break (TALER_AAR_RESULT_ZERO == ret);
    6819          30 :   return reserves_update (cls,
    6820             :                           &reserve);
    6821             : }
    6822             : 
    6823             : 
    6824             : /**
    6825             :  * Function called to insert wire transfer commit data into the DB.
    6826             :  *
    6827             :  * @param cls closure
    6828             :  * @param type type of the wire transfer (i.e. "iban")
    6829             :  * @param buf buffer with wire transfer preparation data
    6830             :  * @param buf_size number of bytes in @a buf
    6831             :  * @return query status code
    6832             :  */
    6833             : static enum GNUNET_DB_QueryStatus
    6834          53 : postgres_wire_prepare_data_insert (void *cls,
    6835             :                                    const char *type,
    6836             :                                    const char *buf,
    6837             :                                    size_t buf_size)
    6838             : {
    6839          53 :   struct PostgresClosure *pg = cls;
    6840          53 :   struct GNUNET_PQ_QueryParam params[] = {
    6841          53 :     GNUNET_PQ_query_param_string (type),
    6842          53 :     GNUNET_PQ_query_param_fixed_size (buf, buf_size),
    6843             :     GNUNET_PQ_query_param_end
    6844             :   };
    6845             : 
    6846          53 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    6847             :                                              "wire_prepare_data_insert",
    6848             :                                              params);
    6849             : }
    6850             : 
    6851             : 
    6852             : /**
    6853             :  * Function called to mark wire transfer commit data as finished.
    6854             :  *
    6855             :  * @param cls closure
    6856             :  * @param rowid which entry to mark as finished
    6857             :  * @return transaction status code
    6858             :  */
    6859             : static enum GNUNET_DB_QueryStatus
    6860          50 : postgres_wire_prepare_data_mark_finished (
    6861             :   void *cls,
    6862             :   uint64_t rowid)
    6863             : {
    6864          50 :   struct PostgresClosure *pg = cls;
    6865          50 :   struct GNUNET_PQ_QueryParam params[] = {
    6866          50 :     GNUNET_PQ_query_param_uint64 (&rowid),
    6867             :     GNUNET_PQ_query_param_end
    6868             :   };
    6869             : 
    6870          50 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    6871             :                                              "wire_prepare_data_mark_done",
    6872             :                                              params);
    6873             : }
    6874             : 
    6875             : 
    6876             : /**
    6877             :  * Function called to mark wire transfer commit data as failed.
    6878             :  *
    6879             :  * @param cls closure
    6880             :  * @param rowid which entry to mark as failed
    6881             :  * @return transaction status code
    6882             :  */
    6883             : static enum GNUNET_DB_QueryStatus
    6884           0 : postgres_wire_prepare_data_mark_failed (
    6885             :   void *cls,
    6886             :   uint64_t rowid)
    6887             : {
    6888           0 :   struct PostgresClosure *pg = cls;
    6889           0 :   struct GNUNET_PQ_QueryParam params[] = {
    6890           0 :     GNUNET_PQ_query_param_uint64 (&rowid),
    6891             :     GNUNET_PQ_query_param_end
    6892             :   };
    6893             : 
    6894           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    6895             :                                              "wire_prepare_data_mark_failed",
    6896             :                                              params);
    6897             : }
    6898             : 
    6899             : 
    6900             : /**
    6901             :  * Function called to get an unfinished wire transfer
    6902             :  * preparation data. Fetches at most one item.
    6903             :  *
    6904             :  * @param cls closure
    6905             :  * @param cb function to call for ONE unfinished item
    6906             :  * @param cb_cls closure for @a cb
    6907             :  * @return transaction status code
    6908             :  */
    6909             : static enum GNUNET_DB_QueryStatus
    6910         101 : postgres_wire_prepare_data_get (void *cls,
    6911             :                                 TALER_EXCHANGEDB_WirePreparationIterator cb,
    6912             :                                 void *cb_cls)
    6913             : {
    6914         101 :   struct PostgresClosure *pg = cls;
    6915             :   enum GNUNET_DB_QueryStatus qs;
    6916         101 :   struct GNUNET_PQ_QueryParam params[] = {
    6917             :     GNUNET_PQ_query_param_end
    6918             :   };
    6919             :   uint64_t prewire_uuid;
    6920             :   char *type;
    6921         101 :   void *buf = NULL;
    6922             :   size_t buf_size;
    6923         101 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6924         101 :     GNUNET_PQ_result_spec_uint64 ("prewire_uuid",
    6925             :                                   &prewire_uuid),
    6926         101 :     GNUNET_PQ_result_spec_string ("type",
    6927             :                                   &type),
    6928         101 :     GNUNET_PQ_result_spec_variable_size ("buf",
    6929             :                                          &buf,
    6930             :                                          &buf_size),
    6931             :     GNUNET_PQ_result_spec_end
    6932             :   };
    6933             : 
    6934         101 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6935             :                                                  "wire_prepare_data_get",
    6936             :                                                  params,
    6937             :                                                  rs);
    6938         101 :   if (0 >= qs)
    6939          51 :     return qs;
    6940          50 :   cb (cb_cls,
    6941             :       prewire_uuid,
    6942             :       type,
    6943             :       buf,
    6944             :       buf_size);
    6945          50 :   GNUNET_PQ_cleanup_result (rs);
    6946          50 :   return qs;
    6947             : }
    6948             : 
    6949             : 
    6950             : /**
    6951             :  * Start a transaction where we transiently violate the foreign
    6952             :  * constraints on the "wire_out" table as we insert aggregations
    6953             :  * and only add the wire transfer out at the end.
    6954             :  *
    6955             :  * @param cls the @e cls of this struct with the plugin-specific state
    6956             :  * @return #GNUNET_OK on success
    6957             :  */
    6958             : static int
    6959          98 : postgres_start_deferred_wire_out (void *cls)
    6960             : {
    6961          98 :   struct PostgresClosure *pg = cls;
    6962          98 :   struct GNUNET_PQ_ExecuteStatement es[] = {
    6963          98 :     GNUNET_PQ_make_execute ("SET CONSTRAINTS wire_out_ref DEFERRED"),
    6964             :     GNUNET_PQ_EXECUTE_STATEMENT_END
    6965             :   };
    6966             : 
    6967          98 :   if (GNUNET_SYSERR ==
    6968          98 :       postgres_preflight (pg))
    6969           0 :     return GNUNET_SYSERR;
    6970          98 :   if (GNUNET_OK !=
    6971          98 :       postgres_start (pg,
    6972             :                       "deferred wire out"))
    6973           0 :     return GNUNET_SYSERR;
    6974          98 :   if (GNUNET_OK !=
    6975          98 :       GNUNET_PQ_exec_statements (pg->conn,
    6976             :                                  es))
    6977             :   {
    6978           0 :     TALER_LOG_ERROR (
    6979             :       "Failed to defer wire_out_ref constraint on transaction\n");
    6980           0 :     GNUNET_break (0);
    6981           0 :     postgres_rollback (pg);
    6982           0 :     return GNUNET_SYSERR;
    6983             :   }
    6984          98 :   return GNUNET_OK;
    6985             : }
    6986             : 
    6987             : 
    6988             : /**
    6989             :  * Store information about an outgoing wire transfer that was executed.
    6990             :  *
    6991             :  * @param cls closure
    6992             :  * @param date time of the wire transfer
    6993             :  * @param wtid subject of the wire transfer
    6994             :  * @param wire_account details about the receiver account of the wire transfer
    6995             :  * @param exchange_account_section configuration section of the exchange specifying the
    6996             :  *        exchange's bank account being used
    6997             :  * @param amount amount that was transmitted
    6998             :  * @return transaction status code
    6999             :  */
    7000             : static enum GNUNET_DB_QueryStatus
    7001          46 : postgres_store_wire_transfer_out (
    7002             :   void *cls,
    7003             :   struct GNUNET_TIME_Absolute date,
    7004             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    7005             :   const json_t *wire_account,
    7006             :   const char *exchange_account_section,
    7007             :   const struct TALER_Amount *amount)
    7008             : {
    7009          46 :   struct PostgresClosure *pg = cls;
    7010          46 :   struct GNUNET_PQ_QueryParam params[] = {
    7011          46 :     TALER_PQ_query_param_absolute_time (&date),
    7012          46 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    7013          46 :     TALER_PQ_query_param_json (wire_account),
    7014          46 :     GNUNET_PQ_query_param_string (exchange_account_section),
    7015          46 :     TALER_PQ_query_param_amount (amount),
    7016             :     GNUNET_PQ_query_param_end
    7017             :   };
    7018             : 
    7019          46 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    7020             :                                              "insert_wire_out",
    7021             :                                              params);
    7022             : }
    7023             : 
    7024             : 
    7025             : /**
    7026             :  * Function called to perform "garbage collection" on the
    7027             :  * database, expiring records we no longer require.
    7028             :  *
    7029             :  * @param cls closure
    7030             :  * @return #GNUNET_OK on success,
    7031             :  *         #GNUNET_SYSERR on DB errors
    7032             :  */
    7033             : static int
    7034          35 : postgres_gc (void *cls)
    7035             : {
    7036          35 :   struct PostgresClosure *pg = cls;
    7037             :   struct GNUNET_TIME_Absolute now;
    7038             :   struct GNUNET_TIME_Absolute long_ago;
    7039          35 :   struct GNUNET_PQ_QueryParam params_none[] = {
    7040             :     GNUNET_PQ_query_param_end
    7041             :   };
    7042          35 :   struct GNUNET_PQ_QueryParam params_time[] = {
    7043          35 :     TALER_PQ_query_param_absolute_time (&now),
    7044             :     GNUNET_PQ_query_param_end
    7045             :   };
    7046          35 :   struct GNUNET_PQ_QueryParam params_ancient_time[] = {
    7047          35 :     TALER_PQ_query_param_absolute_time (&long_ago),
    7048             :     GNUNET_PQ_query_param_end
    7049             :   };
    7050             :   struct GNUNET_PQ_Context *conn;
    7051             :   int ret;
    7052             : 
    7053          35 :   now = GNUNET_TIME_absolute_get ();
    7054          35 :   (void) GNUNET_TIME_round_abs (&now);
    7055             :   /* Keep wire fees for 10 years, that should always
    7056             :      be enough _and_ they are tiny so it does not
    7057             :      matter to make this tight */
    7058          35 :   long_ago = GNUNET_TIME_absolute_subtract (now,
    7059             :                                             GNUNET_TIME_relative_multiply (
    7060             :                                               GNUNET_TIME_UNIT_YEARS,
    7061             :                                               10));
    7062             :   {
    7063          35 :     struct GNUNET_PQ_PreparedStatement ps[] = {
    7064             :       /* Used in #postgres_gc() */
    7065          35 :       GNUNET_PQ_make_prepare ("gc_prewire",
    7066             :                               "DELETE"
    7067             :                               " FROM prewire"
    7068             :                               " WHERE finished=true;",
    7069             :                               0),
    7070          35 :       GNUNET_PQ_make_prepare ("gc_reserves",
    7071             :                               "DELETE"
    7072             :                               " FROM reserves"
    7073             :                               " WHERE gc_date < $1"
    7074             :                               "   AND current_balance_val = 0"
    7075             :                               "   AND current_balance_frac = 0;",
    7076             :                               1),
    7077          35 :       GNUNET_PQ_make_prepare ("gc_wire_fee",
    7078             :                               "DELETE"
    7079             :                               " FROM wire_fee"
    7080             :                               " WHERE end_date < $1;",
    7081             :                               1),
    7082          35 :       GNUNET_PQ_make_prepare ("gc_denominations",
    7083             :                               "DELETE"
    7084             :                               " FROM denominations"
    7085             :                               " WHERE expire_legal < $1;",
    7086             :                               1),
    7087             :       GNUNET_PQ_PREPARED_STATEMENT_END
    7088             :     };
    7089             : 
    7090          35 :     conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
    7091             :                                        "exchangedb-postgres",
    7092             :                                        NULL,
    7093             :                                        NULL,
    7094             :                                        ps);
    7095             :   }
    7096          35 :   if (NULL == conn)
    7097           0 :     return GNUNET_SYSERR;
    7098          35 :   ret = GNUNET_OK;
    7099          35 :   if ( (0 > GNUNET_PQ_eval_prepared_non_select (conn,
    7100             :                                                 "gc_reserves",
    7101          35 :                                                 params_time)) ||
    7102          35 :        (0 > GNUNET_PQ_eval_prepared_non_select (conn,
    7103             :                                                 "gc_prewire",
    7104          35 :                                                 params_none)) ||
    7105          35 :        (0 > GNUNET_PQ_eval_prepared_non_select (conn,
    7106             :                                                 "gc_wire_fee",
    7107             :                                                 params_ancient_time)) )
    7108           0 :     ret = GNUNET_SYSERR;
    7109             :   /* This one may fail due to foreign key constraints from
    7110             :      recoup and reserves_out tables to known_coins; these
    7111             :      are NOT using 'ON DROP CASCADE' and might keep denomination
    7112             :      keys alive for a bit longer, thus causing this statement
    7113             :      to fail. */
    7114          35 :   (void) GNUNET_PQ_eval_prepared_non_select (conn,
    7115             :                                              "gc_denominations",
    7116             :                                              params_time);
    7117          35 :   GNUNET_PQ_disconnect (conn);
    7118          35 :   return ret;
    7119             : }
    7120             : 
    7121             : 
    7122             : /**
    7123             :  * Closure for #deposit_serial_helper_cb().
    7124             :  */
    7125             : struct DepositSerialContext
    7126             : {
    7127             : 
    7128             :   /**
    7129             :    * Callback to call.
    7130             :    */
    7131             :   TALER_EXCHANGEDB_DepositCallback cb;
    7132             : 
    7133             :   /**
    7134             :    * Closure for @e cb.
    7135             :    */
    7136             :   void *cb_cls;
    7137             : 
    7138             :   /**
    7139             :    * Plugin context.
    7140             :    */
    7141             :   struct PostgresClosure *pg;
    7142             : 
    7143             :   /**
    7144             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    7145             :    */
    7146             :   int status;
    7147             : };
    7148             : 
    7149             : 
    7150             : /**
    7151             :  * Helper function to be called with the results of a SELECT statement
    7152             :  * that has returned @a num_results results.
    7153             :  *
    7154             :  * @param cls closure of type `struct DepositSerialContext`
    7155             :  * @param result the postgres result
    7156             :  * @param num_results the number of results in @a result
    7157             :  */
    7158             : static void
    7159          84 : deposit_serial_helper_cb (void *cls,
    7160             :                           PGresult *result,
    7161             :                           unsigned int num_results)
    7162             : {
    7163          84 :   struct DepositSerialContext *dsc = cls;
    7164          84 :   struct PostgresClosure *pg = dsc->pg;
    7165             : 
    7166         209 :   for (unsigned int i = 0; i<num_results; i++)
    7167             :   {
    7168             :     struct TALER_EXCHANGEDB_Deposit deposit;
    7169             :     struct GNUNET_TIME_Absolute exchange_timestamp;
    7170             :     struct TALER_DenominationPublicKey denom_pub;
    7171         125 :     uint8_t done = 0;
    7172             :     uint64_t rowid;
    7173         125 :     struct GNUNET_PQ_ResultSpec rs[] = {
    7174         125 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    7175             :                                    &deposit.amount_with_fee),
    7176         125 :       TALER_PQ_result_spec_absolute_time ("wallet_timestamp",
    7177             :                                           &deposit.timestamp),
    7178         125 :       TALER_PQ_result_spec_absolute_time ("exchange_timestamp",
    7179             :                                           &exchange_timestamp),
    7180         125 :       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    7181             :                                             &deposit.merchant_pub),
    7182         125 :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    7183             :                                             &denom_pub.rsa_public_key),
    7184         125 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    7185             :                                             &deposit.coin.coin_pub),
    7186         125 :       GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    7187             :                                             &deposit.csig),
    7188         125 :       TALER_PQ_result_spec_absolute_time ("refund_deadline",
    7189             :                                           &deposit.refund_deadline),
    7190         125 :       TALER_PQ_result_spec_absolute_time ("wire_deadline",
    7191             :                                           &deposit.wire_deadline),
    7192         125 :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    7193             :                                             &deposit.h_contract_terms),
    7194         125 :       TALER_PQ_result_spec_json ("wire",
    7195             :                                  &deposit.receiver_wire_account),
    7196         125 :       GNUNET_PQ_result_spec_auto_from_type ("done",
    7197             :                                             &done),
    7198         125 :       GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
    7199             :                                     &rowid),
    7200             :       GNUNET_PQ_result_spec_end
    7201             :     };
    7202             :     int ret;
    7203             : 
    7204         125 :     if (GNUNET_OK !=
    7205         125 :         GNUNET_PQ_extract_result (result,
    7206             :                                   rs,
    7207             :                                   i))
    7208             :     {
    7209           0 :       GNUNET_break (0);
    7210           0 :       dsc->status = GNUNET_SYSERR;
    7211           0 :       return;
    7212             :     }
    7213         125 :     ret = dsc->cb (dsc->cb_cls,
    7214             :                    rowid,
    7215             :                    exchange_timestamp,
    7216             :                    deposit.timestamp,
    7217             :                    &deposit.merchant_pub,
    7218             :                    &denom_pub,
    7219             :                    &deposit.coin.coin_pub,
    7220             :                    &deposit.csig,
    7221             :                    &deposit.amount_with_fee,
    7222             :                    &deposit.h_contract_terms,
    7223             :                    deposit.refund_deadline,
    7224             :                    deposit.wire_deadline,
    7225         125 :                    deposit.receiver_wire_account,
    7226             :                    done);
    7227         125 :     GNUNET_PQ_cleanup_result (rs);
    7228         125 :     if (GNUNET_OK != ret)
    7229           0 :       break;
    7230             :   }
    7231             : }
    7232             : 
    7233             : 
    7234             : /**
    7235             :  * Select deposits above @a serial_id in monotonically increasing
    7236             :  * order.
    7237             :  *
    7238             :  * @param cls closure
    7239             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    7240             :  * @param cb function to call on each result
    7241             :  * @param cb_cls closure for @a cb
    7242             :  * @return transaction status code
    7243             :  */
    7244             : static enum GNUNET_DB_QueryStatus
    7245          84 : postgres_select_deposits_above_serial_id (
    7246             :   void *cls,
    7247             :   uint64_t serial_id,
    7248             :   TALER_EXCHANGEDB_DepositCallback cb,
    7249             :   void *cb_cls)
    7250             : {
    7251          84 :   struct PostgresClosure *pg = cls;
    7252          84 :   struct GNUNET_PQ_QueryParam params[] = {
    7253          84 :     GNUNET_PQ_query_param_uint64 (&serial_id),
    7254             :     GNUNET_PQ_query_param_end
    7255             :   };
    7256          84 :   struct DepositSerialContext dsc = {
    7257             :     .cb = cb,
    7258             :     .cb_cls = cb_cls,
    7259             :     .pg = pg,
    7260             :     .status = GNUNET_OK
    7261             :   };
    7262             :   enum GNUNET_DB_QueryStatus qs;
    7263             : 
    7264          84 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    7265             :                                              "audit_get_deposits_incr",
    7266             :                                              params,
    7267             :                                              &deposit_serial_helper_cb,
    7268             :                                              &dsc);
    7269          84 :   if (GNUNET_OK != dsc.status)
    7270           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    7271          84 :   return qs;
    7272             : }
    7273             : 
    7274             : 
    7275             : /**
    7276             :  * Closure for #refreshs_serial_helper_cb().
    7277             :  */
    7278             : struct RefreshsSerialContext
    7279             : {
    7280             : 
    7281             :   /**
    7282             :    * Callback to call.
    7283             :    */
    7284             :   TALER_EXCHANGEDB_RefreshesCallback cb;
    7285             : 
    7286             :   /**
    7287             :    * Closure for @e cb.
    7288             :    */
    7289             :   void *cb_cls;
    7290             : 
    7291             :   /**
    7292             :    * Plugin context.
    7293             :    */
    7294             :   struct PostgresClosure *pg;
    7295             : 
    7296             :   /**
    7297             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    7298             :    */
    7299             :   int status;
    7300             : };
    7301             : 
    7302             : 
    7303             : /**
    7304             :  * Helper function to be called with the results of a SELECT statement
    7305             :  * that has returned @a num_results results.
    7306             :  *
    7307             :  * @param cls closure of type `struct RefreshsSerialContext`
    7308             :  * @param result the postgres result
    7309             :  * @param num_results the number of results in @a result
    7310             :  */
    7311             : static void
    7312          84 : refreshs_serial_helper_cb (void *cls,
    7313             :                            PGresult *result,
    7314             :                            unsigned int num_results)
    7315             : {
    7316          84 :   struct RefreshsSerialContext *rsc = cls;
    7317          84 :   struct PostgresClosure *pg = rsc->pg;
    7318             : 
    7319         236 :   for (unsigned int i = 0; i<num_results; i++)
    7320             :   {
    7321             :     struct TALER_DenominationPublicKey denom_pub;
    7322             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    7323             :     struct TALER_CoinSpendSignatureP coin_sig;
    7324             :     struct TALER_Amount amount_with_fee;
    7325             :     uint32_t noreveal_index;
    7326             :     uint64_t rowid;
    7327             :     struct TALER_RefreshCommitmentP rc;
    7328         152 :     struct GNUNET_PQ_ResultSpec rs[] = {
    7329         152 :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    7330             :                                             &denom_pub.rsa_public_key),
    7331         152 :       GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
    7332             :                                             &coin_pub),
    7333         152 :       GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
    7334             :                                             &coin_sig),
    7335         152 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    7336             :                                    &amount_with_fee),
    7337         152 :       GNUNET_PQ_result_spec_uint32 ("noreveal_index",
    7338             :                                     &noreveal_index),
    7339         152 :       GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
    7340             :                                     &rowid),
    7341         152 :       GNUNET_PQ_result_spec_auto_from_type ("rc",
    7342             :                                             &rc),
    7343             :       GNUNET_PQ_result_spec_end
    7344             :     };
    7345             :     int ret;
    7346             : 
    7347         152 :     if (GNUNET_OK !=
    7348         152 :         GNUNET_PQ_extract_result (result,
    7349             :                                   rs,
    7350             :                                   i))
    7351             :     {
    7352           0 :       GNUNET_break (0);
    7353           0 :       rsc->status = GNUNET_SYSERR;
    7354           0 :       return;
    7355             :     }
    7356         152 :     ret = rsc->cb (rsc->cb_cls,
    7357             :                    rowid,
    7358             :                    &denom_pub,
    7359             :                    &coin_pub,
    7360             :                    &coin_sig,
    7361             :                    &amount_with_fee,
    7362             :                    noreveal_index,
    7363             :                    &rc);
    7364         152 :     GNUNET_PQ_cleanup_result (rs);
    7365         152 :     if (GNUNET_OK != ret)
    7366           0 :       break;
    7367             :   }
    7368             : }
    7369             : 
    7370             : 
    7371             : /**
    7372             :  * Select refresh sessions above @a serial_id in monotonically increasing
    7373             :  * order.
    7374             :  *
    7375             :  * @param cls closure
    7376             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    7377             :  * @param cb function to call on each result
    7378             :  * @param cb_cls closure for @a cb
    7379             :  * @return transaction status code
    7380             :  */
    7381             : static enum GNUNET_DB_QueryStatus
    7382          84 : postgres_select_refreshes_above_serial_id (
    7383             :   void *cls,
    7384             :   uint64_t serial_id,
    7385             :   TALER_EXCHANGEDB_RefreshesCallback cb,
    7386             :   void *cb_cls)
    7387             : {
    7388          84 :   struct PostgresClosure *pg = cls;
    7389          84 :   struct GNUNET_PQ_QueryParam params[] = {
    7390          84 :     GNUNET_PQ_query_param_uint64 (&serial_id),
    7391             :     GNUNET_PQ_query_param_end
    7392             :   };
    7393          84 :   struct RefreshsSerialContext rsc = {
    7394             :     .cb = cb,
    7395             :     .cb_cls = cb_cls,
    7396             :     .pg = pg,
    7397             :     .status = GNUNET_OK
    7398             :   };
    7399             :   enum GNUNET_DB_QueryStatus qs;
    7400             : 
    7401          84 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    7402             :                                              "audit_get_refresh_commitments_incr",
    7403             :                                              params,
    7404             :                                              &refreshs_serial_helper_cb,
    7405             :                                              &rsc);
    7406          84 :   if (GNUNET_OK != rsc.status)
    7407           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    7408          84 :   return qs;
    7409             : }
    7410             : 
    7411             : 
    7412             : /**
    7413             :  * Closure for #refunds_serial_helper_cb().
    7414             :  */
    7415             : struct RefundsSerialContext
    7416             : {
    7417             : 
    7418             :   /**
    7419             :    * Callback to call.
    7420             :    */
    7421             :   TALER_EXCHANGEDB_RefundCallback cb;
    7422             : 
    7423             :   /**
    7424             :    * Closure for @e cb.
    7425             :    */
    7426             :   void *cb_cls;
    7427             : 
    7428             :   /**
    7429             :    * Plugin context.
    7430             :    */
    7431             :   struct PostgresClosure *pg;
    7432             : 
    7433             :   /**
    7434             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    7435             :    */
    7436             :   int status;
    7437             : };
    7438             : 
    7439             : 
    7440             : /**
    7441             :  * Helper function to be called with the results of a SELECT statement
    7442             :  * that has returned @a num_results results.
    7443             :  *
    7444             :  * @param cls closure of type `struct RefundsSerialContext`
    7445             :  * @param result the postgres result
    7446             :  * @param num_results the number of results in @a result
    7447             :  */
    7448             : static void
    7449          84 : refunds_serial_helper_cb (void *cls,
    7450             :                           PGresult *result,
    7451             :                           unsigned int num_results)
    7452             : {
    7453          84 :   struct RefundsSerialContext *rsc = cls;
    7454          84 :   struct PostgresClosure *pg = rsc->pg;
    7455             : 
    7456         119 :   for (unsigned int i = 0; i<num_results; i++)
    7457             :   {
    7458             :     struct TALER_EXCHANGEDB_Refund refund;
    7459             :     struct TALER_DenominationPublicKey denom_pub;
    7460             :     uint64_t rowid;
    7461          35 :     struct GNUNET_PQ_ResultSpec rs[] = {
    7462          35 :       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    7463             :                                             &refund.details.merchant_pub),
    7464          35 :       GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
    7465             :                                             &refund.details.merchant_sig),
    7466          35 :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    7467             :                                             &refund.details.h_contract_terms),
    7468          35 :       GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
    7469             :                                     &refund.details.rtransaction_id),
    7470          35 :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    7471             :                                             &denom_pub.rsa_public_key),
    7472          35 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    7473             :                                             &refund.coin.coin_pub),
    7474          35 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    7475             :                                    &refund.details.refund_amount),
    7476          35 :       GNUNET_PQ_result_spec_uint64 ("refund_serial_id",
    7477             :                                     &rowid),
    7478             :       GNUNET_PQ_result_spec_end
    7479             :     };
    7480             :     int ret;
    7481             : 
    7482          35 :     if (GNUNET_OK !=
    7483          35 :         GNUNET_PQ_extract_result (result,
    7484             :                                   rs,
    7485             :                                   i))
    7486             :     {
    7487           0 :       GNUNET_break (0);
    7488           0 :       rsc->status = GNUNET_SYSERR;
    7489           0 :       return;
    7490             :     }
    7491          35 :     ret = rsc->cb (rsc->cb_cls,
    7492             :                    rowid,
    7493             :                    &denom_pub,
    7494             :                    &refund.coin.coin_pub,
    7495             :                    &refund.details.merchant_pub,
    7496             :                    &refund.details.merchant_sig,
    7497             :                    &refund.details.h_contract_terms,
    7498             :                    refund.details.rtransaction_id,
    7499             :                    &refund.details.refund_amount);
    7500          35 :     GNUNET_PQ_cleanup_result (rs);
    7501          35 :     if (GNUNET_OK != ret)
    7502           0 :       break;
    7503             :   }
    7504             : }
    7505             : 
    7506             : 
    7507             : /**
    7508             :  * Select refunds above @a serial_id in monotonically increasing
    7509             :  * order.
    7510             :  *
    7511             :  * @param cls closure
    7512             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    7513             :  * @param cb function to call on each result
    7514             :  * @param cb_cls closure for @a cb
    7515             :  * @return transaction status code
    7516             :  */
    7517             : static enum GNUNET_DB_QueryStatus
    7518          84 : postgres_select_refunds_above_serial_id (
    7519             :   void *cls,
    7520             :   uint64_t serial_id,
    7521             :   TALER_EXCHANGEDB_RefundCallback cb,
    7522             :   void *cb_cls)
    7523             : {
    7524          84 :   struct PostgresClosure *pg = cls;
    7525          84 :   struct GNUNET_PQ_QueryParam params[] = {
    7526          84 :     GNUNET_PQ_query_param_uint64 (&serial_id),
    7527             :     GNUNET_PQ_query_param_end
    7528             :   };
    7529          84 :   struct RefundsSerialContext rsc = {
    7530             :     .cb = cb,
    7531             :     .cb_cls = cb_cls,
    7532             :     .pg = pg,
    7533             :     .status = GNUNET_OK
    7534             :   };
    7535             :   enum GNUNET_DB_QueryStatus qs;
    7536             : 
    7537          84 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    7538             :                                              "audit_get_refunds_incr",
    7539             :                                              params,
    7540             :                                              &refunds_serial_helper_cb,
    7541             :                                              &rsc);
    7542          84 :   if (GNUNET_OK != rsc.status)
    7543           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    7544          84 :   return qs;
    7545             : }
    7546             : 
    7547             : 
    7548             : /**
    7549             :  * Closure for #reserves_in_serial_helper_cb().
    7550             :  */
    7551             : struct ReservesInSerialContext
    7552             : {
    7553             : 
    7554             :   /**
    7555             :    * Callback to call.
    7556             :    */
    7557             :   TALER_EXCHANGEDB_ReserveInCallback cb;
    7558             : 
    7559             :   /**
    7560             :    * Closure for @e cb.
    7561             :    */
    7562             :   void *cb_cls;
    7563             : 
    7564             :   /**
    7565             :    * Plugin context.
    7566             :    */
    7567             :   struct PostgresClosure *pg;
    7568             : 
    7569             :   /**
    7570             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    7571             :    */
    7572             :   int status;
    7573             : };
    7574             : 
    7575             : 
    7576             : /**
    7577             :  * Helper function to be called with the results of a SELECT statement
    7578             :  * that has returned @a num_results results.
    7579             :  *
    7580             :  * @param cls closure of type `struct ReservesInSerialContext`
    7581             :  * @param result the postgres result
    7582             :  * @param num_results the number of results in @a result
    7583             :  */
    7584             : static void
    7585         167 : reserves_in_serial_helper_cb (void *cls,
    7586             :                               PGresult *result,
    7587             :                               unsigned int num_results)
    7588             : {
    7589         167 :   struct ReservesInSerialContext *risc = cls;
    7590         167 :   struct PostgresClosure *pg = risc->pg;
    7591             : 
    7592         335 :   for (unsigned int i = 0; i<num_results; i++)
    7593             :   {
    7594             :     struct TALER_ReservePublicKeyP reserve_pub;
    7595             :     struct TALER_Amount credit;
    7596             :     char *sender_account_details;
    7597             :     struct GNUNET_TIME_Absolute execution_date;
    7598             :     uint64_t rowid;
    7599             :     uint64_t wire_reference;
    7600         168 :     struct GNUNET_PQ_ResultSpec rs[] = {
    7601         168 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    7602             :                                             &reserve_pub),
    7603         168 :       GNUNET_PQ_result_spec_uint64 ("wire_reference",
    7604             :                                     &wire_reference),
    7605         168 :       TALER_PQ_RESULT_SPEC_AMOUNT ("credit",
    7606             :                                    &credit),
    7607         168 :       TALER_PQ_result_spec_absolute_time ("execution_date",
    7608             :                                           &execution_date),
    7609         168 :       GNUNET_PQ_result_spec_string ("sender_account_details",
    7610             :                                     &sender_account_details),
    7611         168 :       GNUNET_PQ_result_spec_uint64 ("reserve_in_serial_id",
    7612             :                                     &rowid),
    7613             :       GNUNET_PQ_result_spec_end
    7614             :     };
    7615             :     int ret;
    7616             : 
    7617         168 :     if (GNUNET_OK !=
    7618         168 :         GNUNET_PQ_extract_result (result,
    7619             :                                   rs,
    7620             :                                   i))
    7621             :     {
    7622           0 :       GNUNET_break (0);
    7623           0 :       risc->status = GNUNET_SYSERR;
    7624           0 :       return;
    7625             :     }
    7626         168 :     ret = risc->cb (risc->cb_cls,
    7627             :                     rowid,
    7628             :                     &reserve_pub,
    7629             :                     &credit,
    7630             :                     sender_account_details,
    7631             :                     wire_reference,
    7632             :                     execution_date);
    7633         168 :     GNUNET_PQ_cleanup_result (rs);
    7634         168 :     if (GNUNET_OK != ret)
    7635           0 :       break;
    7636             :   }
    7637             : }
    7638             : 
    7639             : 
    7640             : /**
    7641             :  * Select inbound wire transfers into reserves_in above @a serial_id
    7642             :  * in monotonically increasing order.
    7643             :  *
    7644             :  * @param cls closure
    7645             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    7646             :  * @param cb function to call on each result
    7647             :  * @param cb_cls closure for @a cb
    7648             :  * @return transaction status code
    7649             :  */
    7650             : static enum GNUNET_DB_QueryStatus
    7651          84 : postgres_select_reserves_in_above_serial_id (
    7652             :   void *cls,
    7653             :   uint64_t serial_id,
    7654             :   TALER_EXCHANGEDB_ReserveInCallback cb,
    7655             :   void *cb_cls)
    7656             : {
    7657          84 :   struct PostgresClosure *pg = cls;
    7658          84 :   struct GNUNET_PQ_QueryParam params[] = {
    7659          84 :     GNUNET_PQ_query_param_uint64 (&serial_id),
    7660             :     GNUNET_PQ_query_param_end
    7661             :   };
    7662          84 :   struct ReservesInSerialContext risc = {
    7663             :     .cb = cb,
    7664             :     .cb_cls = cb_cls,
    7665             :     .pg = pg,
    7666             :     .status = GNUNET_OK
    7667             :   };
    7668             :   enum GNUNET_DB_QueryStatus qs;
    7669             : 
    7670          84 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    7671             :                                              "audit_reserves_in_get_transactions_incr",
    7672             :                                              params,
    7673             :                                              &reserves_in_serial_helper_cb,
    7674             :                                              &risc);
    7675          84 :   if (GNUNET_OK != risc.status)
    7676           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    7677          84 :   return qs;
    7678             : }
    7679             : 
    7680             : 
    7681             : /**
    7682             :  * Select inbound wire transfers into reserves_in above @a serial_id
    7683             :  * in monotonically increasing order by account.
    7684             :  *
    7685             :  * @param cls closure
    7686             :  * @param account_name name of the account to select by
    7687             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    7688             :  * @param cb function to call on each result
    7689             :  * @param cb_cls closure for @a cb
    7690             :  * @return transaction status code
    7691             :  */
    7692             : static enum GNUNET_DB_QueryStatus
    7693          83 : postgres_select_reserves_in_above_serial_id_by_account (
    7694             :   void *cls,
    7695             :   const char *account_name,
    7696             :   uint64_t serial_id,
    7697             :   TALER_EXCHANGEDB_ReserveInCallback cb,
    7698             :   void *cb_cls)
    7699             : {
    7700          83 :   struct PostgresClosure *pg = cls;
    7701          83 :   struct GNUNET_PQ_QueryParam params[] = {
    7702          83 :     GNUNET_PQ_query_param_uint64 (&serial_id),
    7703          83 :     GNUNET_PQ_query_param_string (account_name),
    7704             :     GNUNET_PQ_query_param_end
    7705             :   };
    7706          83 :   struct ReservesInSerialContext risc = {
    7707             :     .cb = cb,
    7708             :     .cb_cls = cb_cls,
    7709             :     .pg = pg,
    7710             :     .status = GNUNET_OK
    7711             :   };
    7712             :   enum GNUNET_DB_QueryStatus qs;
    7713             : 
    7714          83 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    7715             :                                              "audit_reserves_in_get_transactions_incr_by_account",
    7716             :                                              params,
    7717             :                                              &reserves_in_serial_helper_cb,
    7718             :                                              &risc);
    7719          83 :   if (GNUNET_OK != risc.status)
    7720           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    7721          83 :   return qs;
    7722             : }
    7723             : 
    7724             : 
    7725             : /**
    7726             :  * Closure for #reserves_out_serial_helper_cb().
    7727             :  */
    7728             : struct ReservesOutSerialContext
    7729             : {
    7730             : 
    7731             :   /**
    7732             :    * Callback to call.
    7733             :    */
    7734             :   TALER_EXCHANGEDB_WithdrawCallback cb;
    7735             : 
    7736             :   /**
    7737             :    * Closure for @e cb.
    7738             :    */
    7739             :   void *cb_cls;
    7740             : 
    7741             :   /**
    7742             :    * Plugin context.
    7743             :    */
    7744             :   struct PostgresClosure *pg;
    7745             : 
    7746             :   /**
    7747             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    7748             :    */
    7749             :   int status;
    7750             : };
    7751             : 
    7752             : 
    7753             : /**
    7754             :  * Helper function to be called with the results of a SELECT statement
    7755             :  * that has returned @a num_results results.
    7756             :  *
    7757             :  * @param cls closure of type `struct ReservesOutSerialContext`
    7758             :  * @param result the postgres result
    7759             :  * @param num_results the number of results in @a result
    7760             :  */
    7761             : static void
    7762         167 : reserves_out_serial_helper_cb (void *cls,
    7763             :                                PGresult *result,
    7764             :                                unsigned int num_results)
    7765             : {
    7766         167 :   struct ReservesOutSerialContext *rosc = cls;
    7767         167 :   struct PostgresClosure *pg = rosc->pg;
    7768             : 
    7769        2252 :   for (unsigned int i = 0; i<num_results; i++)
    7770             :   {
    7771             :     struct GNUNET_HashCode h_blind_ev;
    7772             :     struct TALER_DenominationPublicKey denom_pub;
    7773             :     struct TALER_ReservePublicKeyP reserve_pub;
    7774             :     struct TALER_ReserveSignatureP reserve_sig;
    7775             :     struct GNUNET_TIME_Absolute execution_date;
    7776             :     struct TALER_Amount amount_with_fee;
    7777             :     uint64_t rowid;
    7778        2085 :     struct GNUNET_PQ_ResultSpec rs[] = {
    7779        2085 :       GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
    7780             :                                             &h_blind_ev),
    7781        2085 :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    7782             :                                             &denom_pub.rsa_public_key),
    7783        2085 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    7784             :                                             &reserve_pub),
    7785        2085 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
    7786             :                                             &reserve_sig),
    7787        2085 :       TALER_PQ_result_spec_absolute_time ("execution_date",
    7788             :                                           &execution_date),
    7789        2085 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    7790             :                                    &amount_with_fee),
    7791        2085 :       GNUNET_PQ_result_spec_uint64 ("reserve_out_serial_id",
    7792             :                                     &rowid),
    7793             :       GNUNET_PQ_result_spec_end
    7794             :     };
    7795             :     int ret;
    7796             : 
    7797        2085 :     if (GNUNET_OK !=
    7798        2085 :         GNUNET_PQ_extract_result (result,
    7799             :                                   rs,
    7800             :                                   i))
    7801             :     {
    7802           0 :       GNUNET_break (0);
    7803           0 :       rosc->status = GNUNET_SYSERR;
    7804           0 :       return;
    7805             :     }
    7806        2085 :     ret = rosc->cb (rosc->cb_cls,
    7807             :                     rowid,
    7808             :                     &h_blind_ev,
    7809             :                     &denom_pub,
    7810             :                     &reserve_pub,
    7811             :                     &reserve_sig,
    7812             :                     execution_date,
    7813             :                     &amount_with_fee);
    7814        2085 :     GNUNET_PQ_cleanup_result (rs);
    7815        2085 :     if (GNUNET_OK != ret)
    7816           0 :       break;
    7817             :   }
    7818             : }
    7819             : 
    7820             : 
    7821             : /**
    7822             :  * Select withdraw operations from reserves_out above @a serial_id
    7823             :  * in monotonically increasing order.
    7824             :  *
    7825             :  * @param cls closure
    7826             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    7827             :  * @param cb function to call on each result
    7828             :  * @param cb_cls closure for @a cb
    7829             :  * @return transaction status code
    7830             :  */
    7831             : static enum GNUNET_DB_QueryStatus
    7832         167 : postgres_select_withdrawals_above_serial_id (
    7833             :   void *cls,
    7834             :   uint64_t serial_id,
    7835             :   TALER_EXCHANGEDB_WithdrawCallback cb,
    7836             :   void *cb_cls)
    7837             : {
    7838         167 :   struct PostgresClosure *pg = cls;
    7839         167 :   struct GNUNET_PQ_QueryParam params[] = {
    7840         167 :     GNUNET_PQ_query_param_uint64 (&serial_id),
    7841             :     GNUNET_PQ_query_param_end
    7842             :   };
    7843         167 :   struct ReservesOutSerialContext rosc = {
    7844             :     .cb = cb,
    7845             :     .cb_cls = cb_cls,
    7846             :     .pg = pg,
    7847             :     .status = GNUNET_OK
    7848             :   };
    7849             :   enum GNUNET_DB_QueryStatus qs;
    7850             : 
    7851         167 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    7852             :                                              "audit_get_reserves_out_incr",
    7853             :                                              params,
    7854             :                                              &reserves_out_serial_helper_cb,
    7855             :                                              &rosc);
    7856         167 :   if (GNUNET_OK != rosc.status)
    7857           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    7858         167 :   return qs;
    7859             : }
    7860             : 
    7861             : 
    7862             : /**
    7863             :  * Closure for #wire_out_serial_helper_cb().
    7864             :  */
    7865             : struct WireOutSerialContext
    7866             : {
    7867             : 
    7868             :   /**
    7869             :    * Callback to call.
    7870             :    */
    7871             :   TALER_EXCHANGEDB_WireTransferOutCallback cb;
    7872             : 
    7873             :   /**
    7874             :    * Closure for @e cb.
    7875             :    */
    7876             :   void *cb_cls;
    7877             : 
    7878             :   /**
    7879             :    * Plugin context.
    7880             :    */
    7881             :   struct PostgresClosure *pg;
    7882             : 
    7883             :   /**
    7884             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    7885             :    */
    7886             :   int status;
    7887             : };
    7888             : 
    7889             : 
    7890             : /**
    7891             :  * Helper function to be called with the results of a SELECT statement
    7892             :  * that has returned @a num_results results.
    7893             :  *
    7894             :  * @param cls closure of type `struct WireOutSerialContext`
    7895             :  * @param result the postgres result
    7896             :  * @param num_results the number of results in @a result
    7897             :  */
    7898             : static void
    7899         167 : wire_out_serial_helper_cb (void *cls,
    7900             :                            PGresult *result,
    7901             :                            unsigned int num_results)
    7902             : {
    7903         167 :   struct WireOutSerialContext *wosc = cls;
    7904         167 :   struct PostgresClosure *pg = wosc->pg;
    7905             : 
    7906         228 :   for (unsigned int i = 0; i<num_results; i++)
    7907             :   {
    7908             :     uint64_t rowid;
    7909             :     struct GNUNET_TIME_Absolute date;
    7910             :     struct TALER_WireTransferIdentifierRawP wtid;
    7911             :     json_t *wire;
    7912             :     struct TALER_Amount amount;
    7913          61 :     struct GNUNET_PQ_ResultSpec rs[] = {
    7914          61 :       GNUNET_PQ_result_spec_uint64 ("wireout_uuid",
    7915             :                                     &rowid),
    7916          61 :       TALER_PQ_result_spec_absolute_time ("execution_date",
    7917             :                                           &date),
    7918          61 :       GNUNET_PQ_result_spec_auto_from_type ("wtid_raw",
    7919             :                                             &wtid),
    7920          61 :       TALER_PQ_result_spec_json ("wire_target",
    7921             :                                  &wire),
    7922          61 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    7923             :                                    &amount),
    7924             :       GNUNET_PQ_result_spec_end
    7925             :     };
    7926             :     int ret;
    7927             : 
    7928          61 :     if (GNUNET_OK !=
    7929          61 :         GNUNET_PQ_extract_result (result,
    7930             :                                   rs,
    7931             :                                   i))
    7932             :     {
    7933           0 :       GNUNET_break (0);
    7934           0 :       wosc->status = GNUNET_SYSERR;
    7935           0 :       return;
    7936             :     }
    7937          61 :     ret = wosc->cb (wosc->cb_cls,
    7938             :                     rowid,
    7939             :                     date,
    7940             :                     &wtid,
    7941             :                     wire,
    7942             :                     &amount);
    7943          61 :     GNUNET_PQ_cleanup_result (rs);
    7944          61 :     if (GNUNET_OK != ret)
    7945           0 :       break;
    7946             :   }
    7947             : }
    7948             : 
    7949             : 
    7950             : /**
    7951             :  * Function called to select all wire transfers the exchange
    7952             :  * executed.
    7953             :  *
    7954             :  * @param cls closure
    7955             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    7956             :  * @param cb function to call for ONE unfinished item
    7957             :  * @param cb_cls closure for @a cb
    7958             :  * @return transaction status code
    7959             :  */
    7960             : static enum GNUNET_DB_QueryStatus
    7961          84 : postgres_select_wire_out_above_serial_id (
    7962             :   void *cls,
    7963             :   uint64_t serial_id,
    7964             :   TALER_EXCHANGEDB_WireTransferOutCallback cb,
    7965             :   void *cb_cls)
    7966             : {
    7967          84 :   struct PostgresClosure *pg = cls;
    7968          84 :   struct GNUNET_PQ_QueryParam params[] = {
    7969          84 :     GNUNET_PQ_query_param_uint64 (&serial_id),
    7970             :     GNUNET_PQ_query_param_end
    7971             :   };
    7972          84 :   struct WireOutSerialContext wosc = {
    7973             :     .cb = cb,
    7974             :     .cb_cls = cb_cls,
    7975             :     .pg = pg,
    7976             :     .status = GNUNET_OK
    7977             :   };
    7978             :   enum GNUNET_DB_QueryStatus qs;
    7979             : 
    7980          84 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    7981             :                                              "audit_get_wire_incr",
    7982             :                                              params,
    7983             :                                              &wire_out_serial_helper_cb,
    7984             :                                              &wosc);
    7985          84 :   if (GNUNET_OK != wosc.status)
    7986           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    7987          84 :   return qs;
    7988             : }
    7989             : 
    7990             : 
    7991             : /**
    7992             :  * Function called to select all wire transfers the exchange
    7993             :  * executed by account.
    7994             :  *
    7995             :  * @param cls closure
    7996             :  * @param account_name account to select
    7997             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    7998             :  * @param cb function to call for ONE unfinished item
    7999             :  * @param cb_cls closure for @a cb
    8000             :  * @return transaction status code
    8001             :  */
    8002             : static enum GNUNET_DB_QueryStatus
    8003          83 : postgres_select_wire_out_above_serial_id_by_account (
    8004             :   void *cls,
    8005             :   const char *account_name,
    8006             :   uint64_t serial_id,
    8007             :   TALER_EXCHANGEDB_WireTransferOutCallback cb,
    8008             :   void *cb_cls)
    8009             : {
    8010          83 :   struct PostgresClosure *pg = cls;
    8011          83 :   struct GNUNET_PQ_QueryParam params[] = {
    8012          83 :     GNUNET_PQ_query_param_uint64 (&serial_id),
    8013          83 :     GNUNET_PQ_query_param_string (account_name),
    8014             :     GNUNET_PQ_query_param_end
    8015             :   };
    8016          83 :   struct WireOutSerialContext wosc = {
    8017             :     .cb = cb,
    8018             :     .cb_cls = cb_cls,
    8019             :     .pg = pg,
    8020             :     .status = GNUNET_OK
    8021             :   };
    8022             :   enum GNUNET_DB_QueryStatus qs;
    8023             : 
    8024          83 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    8025             :                                              "audit_get_wire_incr_by_account",
    8026             :                                              params,
    8027             :                                              &wire_out_serial_helper_cb,
    8028             :                                              &wosc);
    8029          83 :   if (GNUNET_OK != wosc.status)
    8030           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    8031          83 :   return qs;
    8032             : }
    8033             : 
    8034             : 
    8035             : /**
    8036             :  * Closure for #recoup_serial_helper_cb().
    8037             :  */
    8038             : struct RecoupSerialContext
    8039             : {
    8040             : 
    8041             :   /**
    8042             :    * Callback to call.
    8043             :    */
    8044             :   TALER_EXCHANGEDB_RecoupCallback cb;
    8045             : 
    8046             :   /**
    8047             :    * Closure for @e cb.
    8048             :    */
    8049             :   void *cb_cls;
    8050             : 
    8051             :   /**
    8052             :    * Plugin context.
    8053             :    */
    8054             :   struct PostgresClosure *pg;
    8055             : 
    8056             :   /**
    8057             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    8058             :    */
    8059             :   int status;
    8060             : };
    8061             : 
    8062             : 
    8063             : /**
    8064             :  * Helper function to be called with the results of a SELECT statement
    8065             :  * that has returned @a num_results results.
    8066             :  *
    8067             :  * @param cls closure of type `struct RecoupSerialContext`
    8068             :  * @param result the postgres result
    8069             :  * @param num_results the number of results in @a result
    8070             :  */
    8071             : static void
    8072         168 : recoup_serial_helper_cb (void *cls,
    8073             :                          PGresult *result,
    8074             :                          unsigned int num_results)
    8075             : {
    8076         168 :   struct RecoupSerialContext *psc = cls;
    8077         168 :   struct PostgresClosure *pg = psc->pg;
    8078             : 
    8079         183 :   for (unsigned int i = 0; i<num_results; i++)
    8080             :   {
    8081             :     uint64_t rowid;
    8082             :     struct TALER_ReservePublicKeyP reserve_pub;
    8083             :     struct TALER_CoinPublicInfo coin;
    8084             :     struct TALER_CoinSpendSignatureP coin_sig;
    8085             :     struct TALER_DenominationBlindingKeyP coin_blind;
    8086             :     struct TALER_Amount amount;
    8087             :     struct TALER_DenominationPublicKey denom_pub;
    8088             :     struct GNUNET_HashCode h_blind_ev;
    8089             :     struct GNUNET_TIME_Absolute timestamp;
    8090          15 :     struct GNUNET_PQ_ResultSpec rs[] = {
    8091          15 :       GNUNET_PQ_result_spec_uint64 ("recoup_uuid",
    8092             :                                     &rowid),
    8093          15 :       TALER_PQ_result_spec_absolute_time ("timestamp",
    8094             :                                           &timestamp),
    8095          15 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    8096             :                                             &reserve_pub),
    8097          15 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    8098             :                                             &coin.coin_pub),
    8099          15 :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    8100             :                                             &denom_pub.rsa_public_key),
    8101          15 :       GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    8102             :                                             &coin_sig),
    8103          15 :       GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
    8104             :                                             &coin_blind),
    8105          15 :       GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
    8106             :                                             &h_blind_ev),
    8107          15 :       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    8108             :                                             &coin.denom_pub_hash),
    8109          15 :       GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    8110             :                                            &coin.denom_sig.rsa_signature),
    8111          15 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    8112             :                                    &amount),
    8113             :       GNUNET_PQ_result_spec_end
    8114             :     };
    8115             :     int ret;
    8116             : 
    8117          15 :     if (GNUNET_OK !=
    8118          15 :         GNUNET_PQ_extract_result (result,
    8119             :                                   rs,
    8120             :                                   i))
    8121             :     {
    8122           0 :       GNUNET_break (0);
    8123           0 :       psc->status = GNUNET_SYSERR;
    8124           0 :       return;
    8125             :     }
    8126          15 :     ret = psc->cb (psc->cb_cls,
    8127             :                    rowid,
    8128             :                    timestamp,
    8129             :                    &amount,
    8130             :                    &reserve_pub,
    8131             :                    &coin,
    8132             :                    &denom_pub,
    8133             :                    &coin_sig,
    8134             :                    &coin_blind);
    8135          15 :     GNUNET_PQ_cleanup_result (rs);
    8136          15 :     if (GNUNET_OK != ret)
    8137           0 :       break;
    8138             :   }
    8139             : }
    8140             : 
    8141             : 
    8142             : /**
    8143             :  * Function called to select recoup requests the exchange
    8144             :  * received, ordered by serial ID (monotonically increasing).
    8145             :  *
    8146             :  * @param cls closure
    8147             :  * @param serial_id lowest serial ID to include (select larger or equal)
    8148             :  * @param cb function to call for ONE unfinished item
    8149             :  * @param cb_cls closure for @a cb
    8150             :  * @return transaction status code
    8151             :  */
    8152             : static enum GNUNET_DB_QueryStatus
    8153         168 : postgres_select_recoup_above_serial_id (
    8154             :   void *cls,
    8155             :   uint64_t serial_id,
    8156             :   TALER_EXCHANGEDB_RecoupCallback cb,
    8157             :   void *cb_cls)
    8158             : {
    8159         168 :   struct PostgresClosure *pg = cls;
    8160         168 :   struct GNUNET_PQ_QueryParam params[] = {
    8161         168 :     GNUNET_PQ_query_param_uint64 (&serial_id),
    8162             :     GNUNET_PQ_query_param_end
    8163             :   };
    8164         168 :   struct RecoupSerialContext psc = {
    8165             :     .cb = cb,
    8166             :     .cb_cls = cb_cls,
    8167             :     .pg = pg,
    8168             :     .status = GNUNET_OK
    8169             :   };
    8170             :   enum GNUNET_DB_QueryStatus qs;
    8171             : 
    8172         168 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    8173             :                                              "recoup_get_incr",
    8174             :                                              params,
    8175             :                                              &recoup_serial_helper_cb,
    8176             :                                              &psc);
    8177         168 :   if (GNUNET_OK != psc.status)
    8178           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    8179         168 :   return qs;
    8180             : }
    8181             : 
    8182             : 
    8183             : /**
    8184             :  * Closure for #recoup_refresh_serial_helper_cb().
    8185             :  */
    8186             : struct RecoupRefreshSerialContext
    8187             : {
    8188             : 
    8189             :   /**
    8190             :    * Callback to call.
    8191             :    */
    8192             :   TALER_EXCHANGEDB_RecoupRefreshCallback cb;
    8193             : 
    8194             :   /**
    8195             :    * Closure for @e cb.
    8196             :    */
    8197             :   void *cb_cls;
    8198             : 
    8199             :   /**
    8200             :    * Plugin context.
    8201             :    */
    8202             :   struct PostgresClosure *pg;
    8203             : 
    8204             :   /**
    8205             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    8206             :    */
    8207             :   int status;
    8208             : };
    8209             : 
    8210             : 
    8211             : /**
    8212             :  * Helper function to be called with the results of a SELECT statement
    8213             :  * that has returned @a num_results results.
    8214             :  *
    8215             :  * @param cls closure of type `struct RecoupRefreshSerialContext`
    8216             :  * @param result the postgres result
    8217             :  * @param num_results the number of results in @a result
    8218             :  */
    8219             : static void
    8220          83 : recoup_refresh_serial_helper_cb (void *cls,
    8221             :                                  PGresult *result,
    8222             :                                  unsigned int num_results)
    8223             : {
    8224          83 :   struct RecoupRefreshSerialContext *psc = cls;
    8225          83 :   struct PostgresClosure *pg = psc->pg;
    8226             : 
    8227         123 :   for (unsigned int i = 0; i<num_results; i++)
    8228             :   {
    8229             :     uint64_t rowid;
    8230             :     struct TALER_CoinSpendPublicKeyP old_coin_pub;
    8231             :     struct TALER_CoinPublicInfo coin;
    8232             :     struct TALER_CoinSpendSignatureP coin_sig;
    8233             :     struct TALER_DenominationBlindingKeyP coin_blind;
    8234             :     struct TALER_DenominationPublicKey denom_pub;
    8235             :     struct GNUNET_HashCode old_denom_pub_hash;
    8236             :     struct TALER_Amount amount;
    8237             :     struct GNUNET_HashCode h_blind_ev;
    8238             :     struct GNUNET_TIME_Absolute timestamp;
    8239          40 :     struct GNUNET_PQ_ResultSpec rs[] = {
    8240          40 :       GNUNET_PQ_result_spec_uint64 ("recoup_refresh_uuid",
    8241             :                                     &rowid),
    8242          40 :       TALER_PQ_result_spec_absolute_time ("timestamp",
    8243             :                                           &timestamp),
    8244          40 :       GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
    8245             :                                             &old_coin_pub),
    8246          40 :       GNUNET_PQ_result_spec_auto_from_type ("old_denom_pub_hash",
    8247             :                                             &old_denom_pub_hash),
    8248          40 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    8249             :                                             &coin.coin_pub),
    8250          40 :       GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    8251             :                                             &coin_sig),
    8252          40 :       GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
    8253             :                                             &coin_blind),
    8254          40 :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    8255             :                                             &denom_pub.rsa_public_key),
    8256          40 :       GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
    8257             :                                             &h_blind_ev),
    8258          40 :       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    8259             :                                             &coin.denom_pub_hash),
    8260          40 :       GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    8261             :                                            &coin.denom_sig.rsa_signature),
    8262          40 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    8263             :                                    &amount),
    8264             :       GNUNET_PQ_result_spec_end
    8265             :     };
    8266             :     int ret;
    8267             : 
    8268          40 :     if (GNUNET_OK !=
    8269          40 :         GNUNET_PQ_extract_result (result,
    8270             :                                   rs,
    8271             :                                   i))
    8272             :     {
    8273           0 :       GNUNET_break (0);
    8274           0 :       psc->status = GNUNET_SYSERR;
    8275           0 :       return;
    8276             :     }
    8277          40 :     ret = psc->cb (psc->cb_cls,
    8278             :                    rowid,
    8279             :                    timestamp,
    8280             :                    &amount,
    8281             :                    &old_coin_pub,
    8282             :                    &old_denom_pub_hash,
    8283             :                    &coin,
    8284             :                    &denom_pub,
    8285             :                    &coin_sig,
    8286             :                    &coin_blind);
    8287          40 :     GNUNET_PQ_cleanup_result (rs);
    8288          40 :     if (GNUNET_OK != ret)
    8289           0 :       break;
    8290             :   }
    8291             : }
    8292             : 
    8293             : 
    8294             : /**
    8295             :  * Function called to select recoup requests the exchange received for
    8296             :  * refreshed coins, ordered by serial ID (monotonically increasing).
    8297             :  *
    8298             :  * @param cls closure
    8299             :  * @param serial_id lowest serial ID to include (select larger or equal)
    8300             :  * @param cb function to call for ONE unfinished item
    8301             :  * @param cb_cls closure for @a cb
    8302             :  * @return transaction status code
    8303             :  */
    8304             : static enum GNUNET_DB_QueryStatus
    8305          83 : postgres_select_recoup_refresh_above_serial_id (
    8306             :   void *cls,
    8307             :   uint64_t serial_id,
    8308             :   TALER_EXCHANGEDB_RecoupRefreshCallback cb,
    8309             :   void *cb_cls)
    8310             : {
    8311          83 :   struct PostgresClosure *pg = cls;
    8312          83 :   struct GNUNET_PQ_QueryParam params[] = {
    8313          83 :     GNUNET_PQ_query_param_uint64 (&serial_id),
    8314             :     GNUNET_PQ_query_param_end
    8315             :   };
    8316          83 :   struct RecoupRefreshSerialContext psc = {
    8317             :     .cb = cb,
    8318             :     .cb_cls = cb_cls,
    8319             :     .pg = pg,
    8320             :     .status = GNUNET_OK
    8321             :   };
    8322             :   enum GNUNET_DB_QueryStatus qs;
    8323             : 
    8324          83 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    8325             :                                              "recoup_refresh_get_incr",
    8326             :                                              params,
    8327             :                                              &recoup_refresh_serial_helper_cb,
    8328             :                                              &psc);
    8329          83 :   if (GNUNET_OK != psc.status)
    8330           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    8331          83 :   return qs;
    8332             : }
    8333             : 
    8334             : 
    8335             : /**
    8336             :  * Closure for #reserve_closed_serial_helper_cb().
    8337             :  */
    8338             : struct ReserveClosedSerialContext
    8339             : {
    8340             : 
    8341             :   /**
    8342             :    * Callback to call.
    8343             :    */
    8344             :   TALER_EXCHANGEDB_ReserveClosedCallback cb;
    8345             : 
    8346             :   /**
    8347             :    * Closure for @e cb.
    8348             :    */
    8349             :   void *cb_cls;
    8350             : 
    8351             :   /**
    8352             :    * Plugin's context.
    8353             :    */
    8354             :   struct PostgresClosure *pg;
    8355             : 
    8356             :   /**
    8357             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    8358             :    */
    8359             :   int status;
    8360             : };
    8361             : 
    8362             : 
    8363             : /**
    8364             :  * Helper function to be called with the results of a SELECT statement
    8365             :  * that has returned @a num_results results.
    8366             :  *
    8367             :  * @param cls closure of type `struct ReserveClosedSerialContext`
    8368             :  * @param result the postgres result
    8369             :  * @param num_results the number of results in @a result
    8370             :  */
    8371             : static void
    8372         166 : reserve_closed_serial_helper_cb (void *cls,
    8373             :                                  PGresult *result,
    8374             :                                  unsigned int num_results)
    8375             : {
    8376         166 :   struct ReserveClosedSerialContext *rcsc = cls;
    8377         166 :   struct PostgresClosure *pg = rcsc->pg;
    8378             : 
    8379         255 :   for (unsigned int i = 0; i<num_results; i++)
    8380             :   {
    8381             :     uint64_t rowid;
    8382             :     struct TALER_ReservePublicKeyP reserve_pub;
    8383             :     char *receiver_account;
    8384             :     struct TALER_WireTransferIdentifierRawP wtid;
    8385             :     struct TALER_Amount amount_with_fee;
    8386             :     struct TALER_Amount closing_fee;
    8387             :     struct GNUNET_TIME_Absolute execution_date;
    8388          89 :     struct GNUNET_PQ_ResultSpec rs[] = {
    8389          89 :       GNUNET_PQ_result_spec_uint64 ("close_uuid",
    8390             :                                     &rowid),
    8391          89 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    8392             :                                             &reserve_pub),
    8393          89 :       TALER_PQ_result_spec_absolute_time ("execution_date",
    8394             :                                           &execution_date),
    8395          89 :       GNUNET_PQ_result_spec_auto_from_type ("wtid",
    8396             :                                             &wtid),
    8397          89 :       GNUNET_PQ_result_spec_string ("receiver_account",
    8398             :                                     &receiver_account),
    8399          89 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    8400             :                                    &amount_with_fee),
    8401          89 :       TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
    8402             :                                    &closing_fee),
    8403             :       GNUNET_PQ_result_spec_end
    8404             :     };
    8405             :     int ret;
    8406             : 
    8407          89 :     if (GNUNET_OK !=
    8408          89 :         GNUNET_PQ_extract_result (result,
    8409             :                                   rs,
    8410             :                                   i))
    8411             :     {
    8412           0 :       GNUNET_break (0);
    8413           0 :       rcsc->status = GNUNET_SYSERR;
    8414           0 :       return;
    8415             :     }
    8416          89 :     ret = rcsc->cb (rcsc->cb_cls,
    8417             :                     rowid,
    8418             :                     execution_date,
    8419             :                     &amount_with_fee,
    8420             :                     &closing_fee,
    8421             :                     &reserve_pub,
    8422             :                     receiver_account,
    8423             :                     &wtid);
    8424          89 :     GNUNET_PQ_cleanup_result (rs);
    8425          89 :     if (GNUNET_OK != ret)
    8426           0 :       break;
    8427             :   }
    8428             : }
    8429             : 
    8430             : 
    8431             : /**
    8432             :  * Function called to select reserve close operations the aggregator
    8433             :  * triggered, ordered by serial ID (monotonically increasing).
    8434             :  *
    8435             :  * @param cls closure
    8436             :  * @param serial_id lowest serial ID to include (select larger or equal)
    8437             :  * @param cb function to call for ONE unfinished item
    8438             :  * @param cb_cls closure for @a cb
    8439             :  * @return transaction status code
    8440             :  */
    8441             : static enum GNUNET_DB_QueryStatus
    8442         166 : postgres_select_reserve_closed_above_serial_id (
    8443             :   void *cls,
    8444             :   uint64_t serial_id,
    8445             :   TALER_EXCHANGEDB_ReserveClosedCallback cb,
    8446             :   void *cb_cls)
    8447             : {
    8448         166 :   struct PostgresClosure *pg = cls;
    8449         166 :   struct GNUNET_PQ_QueryParam params[] = {
    8450         166 :     GNUNET_PQ_query_param_uint64 (&serial_id),
    8451             :     GNUNET_PQ_query_param_end
    8452             :   };
    8453         166 :   struct ReserveClosedSerialContext rcsc = {
    8454             :     .cb = cb,
    8455             :     .cb_cls = cb_cls,
    8456             :     .pg = pg,
    8457             :     .status = GNUNET_OK
    8458             :   };
    8459             :   enum GNUNET_DB_QueryStatus qs;
    8460             : 
    8461         166 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    8462             :                                              "reserves_close_get_incr",
    8463             :                                              params,
    8464             :                                              &reserve_closed_serial_helper_cb,
    8465             :                                              &rcsc);
    8466         166 :   if (GNUNET_OK != rcsc.status)
    8467           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    8468         166 :   return qs;
    8469             : }
    8470             : 
    8471             : 
    8472             : /**
    8473             :  * Function called to add a request for an emergency recoup for a
    8474             :  * coin.  The funds are to be added back to the reserve.  The function
    8475             :  * should return the @a deadline by which the exchange will trigger a
    8476             :  * wire transfer back to the customer's account for the reserve.
    8477             :  *
    8478             :  * @param cls closure
    8479             :  * @param reserve_pub public key of the reserve that is being refunded
    8480             :  * @param coin information about the coin
    8481             :  * @param coin_sig signature of the coin of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
    8482             :  * @param coin_blind blinding key of the coin
    8483             :  * @param amount total amount to be paid back
    8484             :  * @param h_blind_ev hash of the blinded coin's envelope (must match reserves_out entry)
    8485             :  * @param timestamp current time (rounded)
    8486             :  * @return transaction result status
    8487             :  */
    8488             : static enum GNUNET_DB_QueryStatus
    8489           7 : postgres_insert_recoup_request (
    8490             :   void *cls,
    8491             :   const struct TALER_ReservePublicKeyP *reserve_pub,
    8492             :   const struct TALER_CoinPublicInfo *coin,
    8493             :   const struct TALER_CoinSpendSignatureP *coin_sig,
    8494             :   const struct TALER_DenominationBlindingKeyP *coin_blind,
    8495             :   const struct TALER_Amount *amount,
    8496             :   const struct GNUNET_HashCode *h_blind_ev,
    8497             :   struct GNUNET_TIME_Absolute timestamp)
    8498             : {
    8499           7 :   struct PostgresClosure *pg = cls;
    8500             :   struct GNUNET_TIME_Absolute expiry;
    8501             :   struct GNUNET_TIME_Absolute gc;
    8502             :   struct TALER_EXCHANGEDB_Reserve reserve;
    8503           7 :   struct GNUNET_PQ_QueryParam params[] = {
    8504           7 :     GNUNET_PQ_query_param_auto_from_type (&coin->coin_pub),
    8505           7 :     GNUNET_PQ_query_param_auto_from_type (coin_sig),
    8506           7 :     GNUNET_PQ_query_param_auto_from_type (coin_blind),
    8507           7 :     TALER_PQ_query_param_amount (amount),
    8508           7 :     TALER_PQ_query_param_absolute_time (&timestamp),
    8509           7 :     GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
    8510             :     GNUNET_PQ_query_param_end
    8511             :   };
    8512             :   enum GNUNET_DB_QueryStatus qs;
    8513             : 
    8514             :   /* now store actual recoup information */
    8515           7 :   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    8516             :                                            "recoup_insert",
    8517             :                                            params);
    8518           7 :   if (0 > qs)
    8519             :   {
    8520           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    8521           0 :     return qs;
    8522             :   }
    8523             : 
    8524             :   /* Update reserve balance */
    8525           7 :   reserve.pub = *reserve_pub;
    8526           7 :   qs = postgres_reserves_get (pg,
    8527             :                               &reserve);
    8528           7 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    8529             :   {
    8530           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    8531           0 :     return qs;
    8532             :   }
    8533           7 :   if (0 >
    8534           7 :       TALER_amount_add (&reserve.balance,
    8535             :                         &reserve.balance,
    8536             :                         amount))
    8537             :   {
    8538           0 :     GNUNET_break (0);
    8539           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    8540             :   }
    8541           7 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    8542             :               "Inserting recoup for coin %s\n",
    8543             :               TALER_B2S (&coin->coin_pub));
    8544           7 :   gc = GNUNET_TIME_absolute_add (timestamp,
    8545             :                                  pg->legal_reserve_expiration_time);
    8546           7 :   reserve.gc = GNUNET_TIME_absolute_max (gc,
    8547             :                                          reserve.gc);
    8548           7 :   (void) GNUNET_TIME_round_abs (&reserve.gc);
    8549           7 :   expiry = GNUNET_TIME_absolute_add (timestamp,
    8550             :                                      pg->idle_reserve_expiration_time);
    8551           7 :   reserve.expiry = GNUNET_TIME_absolute_max (expiry,
    8552             :                                              reserve.expiry);
    8553           7 :   (void) GNUNET_TIME_round_abs (&reserve.expiry);
    8554           7 :   qs = reserves_update (pg,
    8555             :                         &reserve);
    8556           7 :   if (0 >= qs)
    8557             :   {
    8558           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    8559           0 :     return qs;
    8560             :   }
    8561           7 :   return qs;
    8562             : }
    8563             : 
    8564             : 
    8565             : /**
    8566             :  * Function called to add a request for an emergency recoup for a
    8567             :  * refreshed coin.  The funds are to be added back to the original coin
    8568             :  * (which is implied via @a h_blind_ev, see the prepared statement
    8569             :  * "recoup_by_old_coin" used in #postgres_get_coin_transactions()).
    8570             :  *
    8571             :  * @param cls closure
    8572             :  * @param coin public information about the refreshed coin
    8573             :  * @param coin_sig signature of the coin of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
    8574             :  * @param coin_blind blinding key of the coin
    8575             :  * @param h_blind_ev blinded envelope, as calculated by the exchange
    8576             :  * @param amount total amount to be paid back
    8577             :  * @param h_blind_ev hash of the blinded coin's envelope (must match reserves_out entry)
    8578             :  * @param timestamp a timestamp to store
    8579             :  * @return transaction result status
    8580             :  */
    8581             : static enum GNUNET_DB_QueryStatus
    8582           5 : postgres_insert_recoup_refresh_request (
    8583             :   void *cls,
    8584             :   const struct TALER_CoinPublicInfo *coin,
    8585             :   const struct TALER_CoinSpendSignatureP *coin_sig,
    8586             :   const struct TALER_DenominationBlindingKeyP *coin_blind,
    8587             :   const struct TALER_Amount *amount,
    8588             :   const struct GNUNET_HashCode *h_blind_ev,
    8589             :   struct GNUNET_TIME_Absolute timestamp)
    8590             : {
    8591           5 :   struct PostgresClosure *pg = cls;
    8592           5 :   struct GNUNET_PQ_QueryParam params[] = {
    8593           5 :     GNUNET_PQ_query_param_auto_from_type (&coin->coin_pub),
    8594           5 :     GNUNET_PQ_query_param_auto_from_type (coin_sig),
    8595           5 :     GNUNET_PQ_query_param_auto_from_type (coin_blind),
    8596           5 :     TALER_PQ_query_param_amount (amount),
    8597           5 :     TALER_PQ_query_param_absolute_time (&timestamp),
    8598           5 :     GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
    8599             :     GNUNET_PQ_query_param_end
    8600             :   };
    8601             :   enum GNUNET_DB_QueryStatus qs;
    8602             : 
    8603             :   /* now store actual recoup information */
    8604           5 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    8605             :               "Inserting recoup-refresh for coin %s\n",
    8606             :               TALER_B2S (&coin->coin_pub));
    8607           5 :   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    8608             :                                            "recoup_refresh_insert",
    8609             :                                            params);
    8610           5 :   if (0 > qs)
    8611             :   {
    8612           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    8613           0 :     return qs;
    8614             :   }
    8615           5 :   return qs;
    8616             : }
    8617             : 
    8618             : 
    8619             : /**
    8620             :  * Obtain information about which reserve a coin was generated
    8621             :  * from given the hash of the blinded coin.
    8622             :  *
    8623             :  * @param cls closure
    8624             :  * @param h_blind_ev hash of the blinded coin
    8625             :  * @param[out] reserve_pub set to information about the reserve (on success only)
    8626             :  * @return transaction status code
    8627             :  */
    8628             : static enum GNUNET_DB_QueryStatus
    8629           8 : postgres_get_reserve_by_h_blind (void *cls,
    8630             :                                  const struct GNUNET_HashCode *h_blind_ev,
    8631             :                                  struct TALER_ReservePublicKeyP *reserve_pub)
    8632             : {
    8633           8 :   struct PostgresClosure *pg = cls;
    8634           8 :   struct GNUNET_PQ_QueryParam params[] = {
    8635           8 :     GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
    8636             :     GNUNET_PQ_query_param_end
    8637             :   };
    8638           8 :   struct GNUNET_PQ_ResultSpec rs[] = {
    8639           8 :     GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    8640             :                                           reserve_pub),
    8641             :     GNUNET_PQ_result_spec_end
    8642             :   };
    8643             : 
    8644           8 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    8645             :                                                    "reserve_by_h_blind",
    8646             :                                                    params,
    8647             :                                                    rs);
    8648             : }
    8649             : 
    8650             : 
    8651             : /**
    8652             :  * Obtain information about which old coin a coin was refreshed
    8653             :  * given the hash of the blinded (fresh) coin.
    8654             :  *
    8655             :  * @param cls closure
    8656             :  * @param h_blind_ev hash of the blinded coin
    8657             :  * @param[out] old_coin_pub set to information about the old coin (on success only)
    8658             :  * @return transaction status code
    8659             :  */
    8660             : static enum GNUNET_DB_QueryStatus
    8661           9 : postgres_get_old_coin_by_h_blind (void *cls,
    8662             :                                   const struct GNUNET_HashCode *h_blind_ev,
    8663             :                                   struct TALER_CoinSpendPublicKeyP *old_coin_pub)
    8664             : {
    8665           9 :   struct PostgresClosure *pg = cls;
    8666           9 :   struct GNUNET_PQ_QueryParam params[] = {
    8667           9 :     GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
    8668             :     GNUNET_PQ_query_param_end
    8669             :   };
    8670           9 :   struct GNUNET_PQ_ResultSpec rs[] = {
    8671           9 :     GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
    8672             :                                           old_coin_pub),
    8673             :     GNUNET_PQ_result_spec_end
    8674             :   };
    8675             : 
    8676           9 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    8677             :                                                    "old_coin_by_h_blind",
    8678             :                                                    params,
    8679             :                                                    rs);
    8680             : }
    8681             : 
    8682             : 
    8683             : /**
    8684             :  * Store information that a denomination key was revoked
    8685             :  * in the database.
    8686             :  *
    8687             :  * @param cls closure
    8688             :  * @param denom_pub_hash hash of the revoked denomination key
    8689             :  * @param master_sig signature affirming the revocation
    8690             :  * @return transaction status code
    8691             :  */
    8692             : static enum GNUNET_DB_QueryStatus
    8693          10 : postgres_insert_denomination_revocation (
    8694             :   void *cls,
    8695             :   const struct GNUNET_HashCode *denom_pub_hash,
    8696             :   const struct TALER_MasterSignatureP *master_sig)
    8697             : {
    8698          10 :   struct PostgresClosure *pg = cls;
    8699          10 :   struct GNUNET_PQ_QueryParam params[] = {
    8700          10 :     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
    8701          10 :     GNUNET_PQ_query_param_auto_from_type (master_sig),
    8702             :     GNUNET_PQ_query_param_end
    8703             :   };
    8704             : 
    8705          10 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    8706             :                                              "denomination_revocation_insert",
    8707             :                                              params);
    8708             : }
    8709             : 
    8710             : 
    8711             : /**
    8712             :  * Obtain information about a denomination key's revocation from
    8713             :  * the database.
    8714             :  *
    8715             :  * @param cls closure
    8716             :  * @param denom_pub_hash hash of the revoked denomination key
    8717             :  * @param[out] master_sig signature affirming the revocation
    8718             :  * @param[out] rowid row where the information is stored
    8719             :  * @return transaction status code
    8720             :  */
    8721             : static enum GNUNET_DB_QueryStatus
    8722         297 : postgres_get_denomination_revocation (
    8723             :   void *cls,
    8724             :   const struct GNUNET_HashCode *denom_pub_hash,
    8725             :   struct TALER_MasterSignatureP *master_sig,
    8726             :   uint64_t *rowid)
    8727             : {
    8728         297 :   struct PostgresClosure *pg = cls;
    8729         297 :   struct GNUNET_PQ_QueryParam params[] = {
    8730         297 :     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
    8731             :     GNUNET_PQ_query_param_end
    8732             :   };
    8733         297 :   struct GNUNET_PQ_ResultSpec rs[] = {
    8734         297 :     GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig),
    8735         297 :     GNUNET_PQ_result_spec_uint64 ("denom_revocations_serial_id", rowid),
    8736             :     GNUNET_PQ_result_spec_end
    8737             :   };
    8738             : 
    8739         297 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    8740             :                                                    "denomination_revocation_get",
    8741             :                                                    params,
    8742             :                                                    rs);
    8743             : }
    8744             : 
    8745             : 
    8746             : /**
    8747             :  * Closure for #missing_wire_cb().
    8748             :  */
    8749             : struct MissingWireContext
    8750             : {
    8751             :   /**
    8752             :    * Function to call per result.
    8753             :    */
    8754             :   TALER_EXCHANGEDB_WireMissingCallback cb;
    8755             : 
    8756             :   /**
    8757             :    * Closure for @e cb.
    8758             :    */
    8759             :   void *cb_cls;
    8760             : 
    8761             :   /**
    8762             :    * Plugin context.
    8763             :    */
    8764             :   struct PostgresClosure *pg;
    8765             : 
    8766             :   /**
    8767             :    * Set to #GNUNET_SYSERR on error.
    8768             :    */
    8769             :   int status;
    8770             : };
    8771             : 
    8772             : 
    8773             : /**
    8774             :  * Invoke the callback for each result.
    8775             :  *
    8776             :  * @param cls a `struct MissingWireContext *`
    8777             :  * @param result SQL result
    8778             :  * @param num_results number of rows in @a result
    8779             :  */
    8780             : static void
    8781          84 : missing_wire_cb (void *cls,
    8782             :                  PGresult *result,
    8783             :                  unsigned int num_results)
    8784             : {
    8785          84 :   struct MissingWireContext *mwc = cls;
    8786          84 :   struct PostgresClosure *pg = mwc->pg;
    8787             : 
    8788         127 :   while (0 < num_results)
    8789             :   {
    8790             :     uint64_t rowid;
    8791             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    8792             :     struct TALER_Amount amount;
    8793             :     json_t *wire;
    8794             :     struct GNUNET_TIME_Absolute deadline;
    8795             :     uint8_t tiny;
    8796             :     uint8_t done;
    8797          43 :     struct GNUNET_PQ_ResultSpec rs[] = {
    8798          43 :       GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
    8799             :                                     &rowid),
    8800          43 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    8801             :                                             &coin_pub),
    8802          43 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    8803             :                                    &amount),
    8804          43 :       TALER_PQ_result_spec_json ("wire",
    8805             :                                  &wire),
    8806          43 :       TALER_PQ_result_spec_absolute_time ("wire_deadline",
    8807             :                                           &deadline),
    8808          43 :       GNUNET_PQ_result_spec_auto_from_type ("tiny",
    8809             :                                             &tiny),
    8810          43 :       GNUNET_PQ_result_spec_auto_from_type ("done",
    8811             :                                             &done),
    8812             :       GNUNET_PQ_result_spec_end
    8813             :     };
    8814             : 
    8815          43 :     if (GNUNET_OK !=
    8816          43 :         GNUNET_PQ_extract_result (result,
    8817             :                                   rs,
    8818             :                                   --num_results))
    8819             :     {
    8820           0 :       GNUNET_break (0);
    8821           0 :       mwc->status = GNUNET_SYSERR;
    8822           0 :       return;
    8823             :     }
    8824          43 :     mwc->cb (mwc->cb_cls,
    8825             :              rowid,
    8826             :              &coin_pub,
    8827             :              &amount,
    8828             :              wire,
    8829             :              deadline,
    8830             :              tiny,
    8831             :              done);
    8832          43 :     GNUNET_PQ_cleanup_result (rs);
    8833             :   }
    8834             : }
    8835             : 
    8836             : 
    8837             : /**
    8838             :  * Select all of those deposits in the database for which we do
    8839             :  * not have a wire transfer (or a refund) and which should have
    8840             :  * been deposited between @a start_date and @a end_date.
    8841             :  *
    8842             :  * @param cls closure
    8843             :  * @param start_date lower bound on the requested wire execution date
    8844             :  * @param end_date upper bound on the requested wire execution date
    8845             :  * @param cb function to call on all such deposits
    8846             :  * @param cb_cls closure for @a cb
    8847             :  * @return transaction status code
    8848             :  */
    8849             : static enum GNUNET_DB_QueryStatus
    8850          84 : postgres_select_deposits_missing_wire (void *cls,
    8851             :                                        struct GNUNET_TIME_Absolute start_date,
    8852             :                                        struct GNUNET_TIME_Absolute end_date,
    8853             :                                        TALER_EXCHANGEDB_WireMissingCallback cb,
    8854             :                                        void *cb_cls)
    8855             : {
    8856          84 :   struct PostgresClosure *pg = cls;
    8857          84 :   struct GNUNET_PQ_QueryParam params[] = {
    8858          84 :     TALER_PQ_query_param_absolute_time (&start_date),
    8859          84 :     TALER_PQ_query_param_absolute_time (&end_date),
    8860             :     GNUNET_PQ_query_param_end
    8861             :   };
    8862          84 :   struct MissingWireContext mwc = {
    8863             :     .cb = cb,
    8864             :     .cb_cls = cb_cls,
    8865             :     .pg = pg,
    8866             :     .status = GNUNET_OK
    8867             :   };
    8868             :   enum GNUNET_DB_QueryStatus qs;
    8869             : 
    8870          84 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    8871             :                                              "deposits_get_overdue",
    8872             :                                              params,
    8873             :                                              &missing_wire_cb,
    8874             :                                              &mwc);
    8875          84 :   if (GNUNET_OK != mwc.status)
    8876           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    8877          84 :   return qs;
    8878             : }
    8879             : 
    8880             : 
    8881             : /**
    8882             :  * Check the last date an auditor was modified.
    8883             :  *
    8884             :  * @param cls closure
    8885             :  * @param auditor_pub key to look up information for
    8886             :  * @param[out] last_date last modification date to auditor status
    8887             :  * @return transaction status code
    8888             :  */
    8889             : static enum GNUNET_DB_QueryStatus
    8890          11 : postgres_lookup_auditor_timestamp (
    8891             :   void *cls,
    8892             :   const struct TALER_AuditorPublicKeyP *auditor_pub,
    8893             :   struct GNUNET_TIME_Absolute *last_date)
    8894             : {
    8895          11 :   struct PostgresClosure *pg = cls;
    8896          11 :   struct GNUNET_PQ_QueryParam params[] = {
    8897          11 :     GNUNET_PQ_query_param_auto_from_type (auditor_pub),
    8898             :     GNUNET_PQ_query_param_end
    8899             :   };
    8900          11 :   struct GNUNET_PQ_ResultSpec rs[] = {
    8901          11 :     TALER_PQ_result_spec_absolute_time ("last_change",
    8902             :                                         last_date),
    8903             :     GNUNET_PQ_result_spec_end
    8904             :   };
    8905             : 
    8906          11 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    8907             :                                                    "lookup_auditor_timestamp",
    8908             :                                                    params,
    8909             :                                                    rs);
    8910             : }
    8911             : 
    8912             : 
    8913             : /**
    8914             :  * Lookup current state of an auditor.
    8915             :  *
    8916             :  * @param cls closure
    8917             :  * @param auditor_pub key to look up information for
    8918             :  * @param[out] auditor_url set to the base URL of the auditor's REST API; memory to be
    8919             :  *            released by the caller!
    8920             :  * @param[out] enabled set if the auditor is currently in use
    8921             :  * @return transaction status code
    8922             :  */
    8923             : static enum GNUNET_DB_QueryStatus
    8924           0 : postgres_lookup_auditor_status (
    8925             :   void *cls,
    8926             :   const struct TALER_AuditorPublicKeyP *auditor_pub,
    8927             :   char **auditor_url,
    8928             :   bool *enabled)
    8929             : {
    8930           0 :   struct PostgresClosure *pg = cls;
    8931           0 :   struct GNUNET_PQ_QueryParam params[] = {
    8932           0 :     GNUNET_PQ_query_param_auto_from_type (auditor_pub),
    8933             :     GNUNET_PQ_query_param_end
    8934             :   };
    8935           0 :   uint8_t enabled8 = 0;
    8936