LCOV - code coverage report
Current view: top level - exchangedb - plugin_exchangedb_postgres.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 946 1195 79.2 %
Date: 2017-09-17 17:24:28 Functions: 89 92 96.7 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014, 2015, 2016, 2017 GNUnet e.V.
       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_pq_lib.h"
      27             : #include "taler_exchangedb_plugin.h"
      28             : #include <pthread.h>
      29             : #include <libpq-fe.h>
      30             : 
      31             : #include "plugin_exchangedb_common.c"
      32             : 
      33             : 
      34             : /**
      35             :  * Log a really unexpected PQ error with all the details we can get hold of.
      36             :  *
      37             :  * @param result PQ result object of the PQ operation that failed
      38             :  * @param conn SQL connection that was used
      39             :  */
      40             : #define BREAK_DB_ERR(result,conn) do {                                      \
      41             :     GNUNET_break (0); \
      42             :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \
      43             :                 "Database failure: %s/%s/%s/%s/%s", \
      44             :                 PQresultErrorField (result, PG_DIAG_MESSAGE_PRIMARY), \
      45             :                 PQresultErrorField (result, PG_DIAG_MESSAGE_DETAIL), \
      46             :                 PQresultErrorMessage (result), \
      47             :                 PQresStatus (PQresultStatus (result)), \
      48             :                 PQerrorMessage (conn)); \
      49             :   } while (0)
      50             : 
      51             : 
      52             : /**
      53             :  * Handle for a database session (per-thread, for transactions).
      54             :  */
      55             : struct TALER_EXCHANGEDB_Session
      56             : {
      57             :   /**
      58             :    * Postgres connection handle.
      59             :    */
      60             :   PGconn *conn;
      61             : 
      62             : };
      63             : 
      64             : 
      65             : /**
      66             :  * Type of the "cls" argument given to each of the functions in
      67             :  * our API.
      68             :  */
      69             : struct PostgresClosure
      70             : {
      71             : 
      72             :   /**
      73             :    * Thread-local database connection.
      74             :    * Contains a pointer to `PGconn` or NULL.
      75             :    */
      76             :   pthread_key_t db_conn_threadlocal;
      77             : 
      78             :   /**
      79             :    * Database connection string, as read from
      80             :    * the configuration.
      81             :    */
      82             :   char *connection_cfg_str;
      83             : 
      84             :   /**
      85             :    * After how long should idle reserves be closed?
      86             :    */
      87             :   struct GNUNET_TIME_Relative idle_reserve_expiration_time;
      88             : };
      89             : 
      90             : 
      91             : /**
      92             :  * Drop all Taler tables.  This should only be used by testcases.
      93             :  *
      94             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
      95             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
      96             :  */
      97             : static int
      98           7 : postgres_drop_tables (void *cls)
      99             : {
     100           7 :   struct PostgresClosure *pc = cls;
     101           7 :   struct GNUNET_PQ_ExecuteStatement es[] = {
     102             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS prewire CASCADE;"),
     103             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS payback CASCADE;"),
     104             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS aggregation_tracking CASCADE;"),
     105             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS wire_out CASCADE;"),
     106             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS wire_fee CASCADE;"),
     107             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS deposits CASCADE;"),
     108             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_out CASCADE;"),
     109             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_commit_coin CASCADE;"),
     110             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_transfer_public_key CASCADE;"),
     111             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refunds CASCADE;"),
     112             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_order CASCADE;"),
     113             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_sessions CASCADE;"),
     114             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS known_coins CASCADE;"),
     115             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS reserves_close CASCADE;"),
     116             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS reserves_out CASCADE;"),
     117             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS reserves_in CASCADE;"),
     118             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS reserves CASCADE;"),
     119             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS denomination_revocations CASCADE;"),
     120             :     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS denominations CASCADE;"),
     121             :     GNUNET_PQ_EXECUTE_STATEMENT_END
     122             :   };
     123             :   PGconn *conn;
     124             :   int ret;
     125             : 
     126           7 :   conn = GNUNET_PQ_connect (pc->connection_cfg_str);
     127           7 :   if (NULL == conn)
     128           0 :     return GNUNET_SYSERR;
     129           7 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     130             :               "Dropping ALL tables\n");
     131           7 :   ret = GNUNET_PQ_exec_statements (conn,
     132             :                                    es);
     133           7 :   PQfinish (conn);
     134           7 :   return ret;
     135             : }
     136             : 
     137             : 
     138             : /**
     139             :  * Create the necessary tables if they are not present
     140             :  *
     141             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     142             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
     143             :  */
     144             : static int
     145          36 : postgres_create_tables (void *cls)
     146             : {
     147          36 :   struct PostgresClosure *pc = cls;
     148          36 :   struct GNUNET_PQ_ExecuteStatement es[] = {
     149             :     /* Denomination table for holding the publicly available information of
     150             :        denominations keys.  The denominations are to be referred to using
     151             :        foreign keys. */
     152             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS denominations"
     153             :                             "(denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64)"
     154             :                             ",denom_pub BYTEA NOT NULL"
     155             :                             ",master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
     156             :                             ",master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)"
     157             :                             ",valid_from INT8 NOT NULL"
     158             :                             ",expire_withdraw INT8 NOT NULL"
     159             :                             ",expire_deposit INT8 NOT NULL"
     160             :                             ",expire_legal INT8 NOT NULL"
     161             :                             ",coin_val INT8 NOT NULL" /* value of this denom */
     162             :                             ",coin_frac INT4 NOT NULL" /* fractional value of this denom */
     163             :                             ",coin_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" /* assuming same currency for fees */
     164             :                             ",fee_withdraw_val INT8 NOT NULL"
     165             :                             ",fee_withdraw_frac INT4 NOT NULL"
     166             :                             ",fee_withdraw_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     167             :                             ",fee_deposit_val INT8 NOT NULL"
     168             :                             ",fee_deposit_frac INT4 NOT NULL"
     169             :                             ",fee_deposit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     170             :                             ",fee_refresh_val INT8 NOT NULL"
     171             :                             ",fee_refresh_frac INT4 NOT NULL"
     172             :                             ",fee_refresh_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     173             :                             ",fee_refund_val INT8 NOT NULL"
     174             :                             ",fee_refund_frac INT4 NOT NULL"
     175             :                             ",fee_refund_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     176             :                             ")"),
     177             :     /* index for gc_denominations */
     178             :     GNUNET_PQ_make_try_execute ("CREATE INDEX denominations_expire_legal_index ON "
     179             :                                 "denominations (expire_legal);"),
     180             : 
     181             :     /* denomination_revocations table is for remembering which denomination keys have been revoked */
     182             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS denomination_revocations"
     183             :                             "(denom_revocations_serial_id BIGSERIAL UNIQUE"
     184             :                             ",denom_pub_hash BYTEA PRIMARY KEY REFERENCES denominations (denom_pub_hash) ON DELETE CASCADE"
     185             :                             ",master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)"
     186             :                             ");"),
     187             :     /* reserves table is for summarization of a reserve.  It is updated when new
     188             :        funds are added and existing funds are withdrawn.  The 'expiration_date'
     189             :        can be used to eventually get rid of reserves that have not been used
     190             :        for a very long time (either by refunding the owner or by greedily
     191             :        grabbing the money, depending on the Exchange's terms of service) */
     192             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS reserves"
     193             :                             "(reserve_pub BYTEA PRIMARY KEY CHECK(LENGTH(reserve_pub)=32)"
     194             :                             ",account_details TEXT NOT NULL "
     195             :                             ",current_balance_val INT8 NOT NULL"
     196             :                             ",current_balance_frac INT4 NOT NULL"
     197             :                             ",current_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     198             :                             ",expiration_date INT8 NOT NULL"
     199             :                             ");"),
     200             :     /* index on reserves table */
     201             :     GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_reserve_pub_index ON "
     202             :                                 "reserves (reserve_pub);"),
     203             :     /* index for get_expired_reserves */
     204             :     GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_expiration_index"
     205             :                                 " ON reserves (expiration_date,current_balance_val,current_balance_frac);"),
     206             :     /* reserves_in table collects the transactions which transfer funds
     207             :        into the reserve.  The rows of this table correspond to each
     208             :        incoming transaction. */
     209             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS reserves_in"
     210             :                            "(reserve_in_serial_id BIGSERIAL UNIQUE"
     211             :                            ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
     212             :                            ",wire_reference BYTEA NOT NULL"
     213             :                            ",credit_val INT8 NOT NULL"
     214             :                            ",credit_frac INT4 NOT NULL"
     215             :                            ",credit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     216             :                            ",sender_account_details TEXT NOT NULL"
     217             :                            ",execution_date INT8 NOT NULL"
     218             :                            ",PRIMARY KEY (reserve_pub, wire_reference)"
     219             :                            ");"),
     220             :     /* Create indices on reserves_in */
     221             :     GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_in_execution_index"
     222             :                                 " ON reserves_in (execution_date);"),
     223             :     /* This table contains the data for wire transfers the exchange has
     224             :        executed to close a reserve. */
     225             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS reserves_close "
     226             :                            "(close_uuid BIGSERIAL PRIMARY KEY"
     227             :                            ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
     228             :                            ",execution_date INT8 NOT NULL"
     229             :                            ",wtid BYTEA NOT NULL CHECK (LENGTH(wtid)=32)"
     230             :                            ",receiver_account TEXT NOT NULL"
     231             :                            ",amount_val INT8 NOT NULL"
     232             :                            ",amount_frac INT4 NOT NULL"
     233             :                            ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     234             :                            ",closing_fee_val INT8 NOT NULL"
     235             :                            ",closing_fee_frac INT4 NOT NULL"
     236             :                            ",closing_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     237             :                            ");"),
     238             :     GNUNET_PQ_make_try_execute("CREATE INDEX reserves_close_by_reserve "
     239             :                                "ON reserves_close(reserve_pub)"),
     240             :     /* Table with the withdraw operations that have been performed on a reserve.
     241             :        The 'h_blind_ev' is the hash of the blinded coin. It serves as a primary
     242             :        key, as (broken) clients that use a non-random coin and blinding factor
     243             :        should fail to even withdraw, as otherwise the coins will fail to deposit
     244             :        (as they really must be unique). */
     245             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS reserves_out"
     246             :                             "(reserve_out_serial_id BIGSERIAL UNIQUE"
     247             :                             ",h_blind_ev BYTEA PRIMARY KEY"
     248             :                             ",denom_pub_hash BYTEA NOT NULL REFERENCES denominations (denom_pub_hash)" /* do NOT CASCADE on DELETE, we may keep the denomination key alive! */
     249             :                             ",denom_sig BYTEA NOT NULL"
     250             :                             ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
     251             :                             ",reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)"
     252             :                             ",execution_date INT8 NOT NULL"
     253             :                             ",amount_with_fee_val INT8 NOT NULL"
     254             :                             ",amount_with_fee_frac INT4 NOT NULL"
     255             :                             ",amount_with_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     256             :                             ");"),
     257             :     /* Index blindcoins(reserve_pub) for get_reserves_out statement */
     258             :     GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_out_reserve_pub_index ON"
     259             :                                 " reserves_out (reserve_pub)"),
     260             :     GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_out_execution_date ON "
     261             :                                 "reserves_out (execution_date)"),
     262             :     /* Table with coins that have been (partially) spent, used to track
     263             :        coin information only once. */
     264             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS known_coins "
     265             :                            "(coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (LENGTH(coin_pub)=32)"
     266             :                            ",denom_pub_hash BYTEA NOT NULL REFERENCES denominations (denom_pub_hash) ON DELETE CASCADE"
     267             :                            ",denom_sig BYTEA NOT NULL"
     268             :                            ");"),
     269             :     /**
     270             :      * The DB will show negative values for some values of the following fields as
     271             :      * we use them as 16 bit unsigned integers
     272             :      *   @a num_newcoins
     273             :      *   @a noreveal_index
     274             :      * Do not do arithmetic in SQL on these fields.
     275             :      * NOTE: maybe we should instead forbid values >= 2^15 categorically?
     276             :      */
     277             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_sessions "
     278             :                            "(melt_serial_id BIGSERIAL UNIQUE"
     279             :                            ",session_hash BYTEA PRIMARY KEY CHECK (LENGTH(session_hash)=64)"
     280             :                            ",old_coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE"
     281             :                            ",old_coin_sig BYTEA NOT NULL CHECK(LENGTH(old_coin_sig)=64)"
     282             :                            ",amount_with_fee_val INT8 NOT NULL"
     283             :                            ",amount_with_fee_frac INT4 NOT NULL"
     284             :                            ",amount_with_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     285             :                            ",num_newcoins INT2 NOT NULL"
     286             :                            ",noreveal_index INT2 NOT NULL"
     287             :                            ");"),
     288             :     GNUNET_PQ_make_try_execute ("CREATE INDEX refresh_sessions_old_coin_pub_index ON "
     289             :                                 "refresh_sessions (old_coin_pub);"),
     290             : 
     291             :     /* Table with information about the desired denominations to be created
     292             :        during a refresh operation; contains the denomination key for each
     293             :        of the coins (for a given refresh session) */
     294             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_order "
     295             :                            "(session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) ON DELETE CASCADE"
     296             :                            ",newcoin_index INT2 NOT NULL "
     297             :                            ",denom_pub_hash BYTEA NOT NULL REFERENCES denominations (denom_pub_hash) ON DELETE CASCADE"
     298             :                            ",PRIMARY KEY (session_hash, newcoin_index)"
     299             :                            ");"),
     300             :     /* Table with the commitments for a refresh operation; includes
     301             :        the session_hash for which this is the link information, the
     302             :        oldcoin index and the cut-and-choose index (from 0 to #TALER_CNC_KAPPA-1),
     303             :        as well as the actual link data (the transfer public key and the encrypted
     304             :        link secret) */
     305             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_transfer_public_key "
     306             :                            "(session_hash BYTEA NOT NULL PRIMARY KEY REFERENCES refresh_sessions (session_hash) ON DELETE CASCADE"
     307             :                            ",transfer_pub BYTEA NOT NULL CHECK(LENGTH(transfer_pub)=32)"
     308             :                            ");"),
     309             :     /* Table with the commitments for the new coins that are to be created
     310             :        during a melting session.  Includes the session, the cut-and-choose
     311             :        index and the index of the new coin, and the envelope of the new
     312             :        coin to be signed, as well as the encrypted information about the
     313             :        private key and the blinding factor for the coin (for verification
     314             :        in case this newcoin_index is chosen to be revealed) */
     315             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_commit_coin "
     316             :                            "(session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) ON DELETE CASCADE"
     317             :                            ",newcoin_index INT2 NOT NULL"
     318             :                            ",coin_ev BYTEA NOT NULL"
     319             :                            ",PRIMARY KEY (session_hash, newcoin_index)"
     320             :                            ");"),
     321             :     /* Table with the signatures over coins generated during a refresh
     322             :        operation. Needed to answer /refresh/link queries later.  Stores
     323             :        the coin signatures under the respective session hash and index. */
     324             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_out "
     325             :                            "(session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) ON DELETE CASCADE"
     326             :                            ",newcoin_index INT2 NOT NULL"
     327             :                            ",ev_sig BYTEA NOT NULL"
     328             :                            ",PRIMARY KEY (session_hash, newcoin_index)"
     329             :                            ");"),
     330             :     /* This table contains the wire transfers the exchange is supposed to
     331             :        execute to transmit funds to the merchants (and manage refunds). */
     332             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS deposits "
     333             :                            "(deposit_serial_id BIGSERIAL PRIMARY KEY"
     334             :                            ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE"
     335             :                            ",amount_with_fee_val INT8 NOT NULL"
     336             :                            ",amount_with_fee_frac INT4 NOT NULL"
     337             :                            ",amount_with_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     338             :                            ",timestamp INT8 NOT NULL"
     339             :                            ",refund_deadline INT8 NOT NULL"
     340             :                            ",wire_deadline INT8 NOT NULL"
     341             :                            ",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)"
     342             :                            ",h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)"
     343             :                            ",h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)"
     344             :                            ",coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)"
     345             :                            ",wire TEXT NOT NULL"
     346             :                            ",tiny BOOLEAN NOT NULL DEFAULT false"
     347             :                            ",done BOOLEAN NOT NULL DEFAULT false"
     348             :                            ",UNIQUE (coin_pub, h_contract_terms, merchant_pub)"
     349             :                            ");"),
     350             :     /* Index for get_deposit statement on coin_pub, h_contract_terms and merchant_pub */
     351             :     GNUNET_PQ_make_try_execute("CREATE INDEX deposits_coin_pub_index "
     352             :                                "ON deposits(coin_pub, h_contract_terms, merchant_pub)"),
     353             :     /* Index for get_deposit_for_wtid */
     354             :     GNUNET_PQ_make_try_execute("CREATE INDEX deposits_coin_pub_merchant_contract_index "
     355             :                                "ON deposits(coin_pub, merchant_pub, h_contract_terms)"),
     356             :     /* Index for deposits_get_ready */
     357             :     GNUNET_PQ_make_try_execute("CREATE INDEX deposits_get_ready_index "
     358             :                                "ON deposits(tiny,done,wire_deadline,refund_deadline)"),
     359             :     /* Index for deposits_iterate_matching */
     360             :     GNUNET_PQ_make_try_execute("CREATE INDEX deposits_iterate_matching "
     361             :                                "ON deposits(merchant_pub,h_wire,done,wire_deadline)"),
     362             : 
     363             :     /* Table with information about coins that have been refunded. (Technically
     364             :        one of the deposit operations that a coin was involved with is refunded.)*/
     365             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refunds "
     366             :                            "(refund_serial_id BIGSERIAL UNIQUE"
     367             :                            ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE"
     368             :                            ",merchant_pub BYTEA NOT NULL CHECK(LENGTH(merchant_pub)=32)"
     369             :                            ",merchant_sig BYTEA NOT NULL CHECK(LENGTH(merchant_sig)=64)"
     370             :                            ",h_contract_terms BYTEA NOT NULL CHECK(LENGTH(h_contract_terms)=64)"
     371             :                            ",rtransaction_id INT8 NOT NULL"
     372             :                            ",amount_with_fee_val INT8 NOT NULL"
     373             :                            ",amount_with_fee_frac INT4 NOT NULL"
     374             :                            ",amount_with_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     375             :                            ",PRIMARY KEY (coin_pub, merchant_pub, h_contract_terms, rtransaction_id)" /* this combo must be unique, and we usually select by coin_pub */
     376             :                            ");"),
     377             :     GNUNET_PQ_make_try_execute("CREATE INDEX refunds_coin_pub_index "
     378             :                                "ON refunds(coin_pub)"),    
     379             :     /* This table contains the data for
     380             :        wire transfers the exchange has executed. */
     381             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS wire_out "
     382             :                            "(wireout_uuid BIGSERIAL PRIMARY KEY"
     383             :                            ",execution_date INT8 NOT NULL"
     384             :                            ",wtid_raw BYTEA UNIQUE NOT NULL CHECK (LENGTH(wtid_raw)=" TALER_WIRE_TRANSFER_IDENTIFIER_LEN_STR ")"
     385             :                            ",wire_target TEXT NOT NULL"
     386             :                            ",amount_val INT8 NOT NULL"
     387             :                            ",amount_frac INT4 NOT NULL"
     388             :                            ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     389             :                            ");"),
     390             :     /* Table for the tracking API, mapping from wire transfer identifiers
     391             :        to transactions and back */
     392             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS aggregation_tracking "
     393             :                            "(aggregation_serial_id BIGSERIAL UNIQUE"
     394             :                            ",deposit_serial_id INT8 PRIMARY KEY REFERENCES deposits (deposit_serial_id) ON DELETE CASCADE"
     395             :                            ",wtid_raw BYTEA  CONSTRAINT wire_out_ref REFERENCES wire_out(wtid_raw) ON DELETE CASCADE DEFERRABLE"
     396             :                            ");"),
     397             :     /* Index for lookup_transactions statement on wtid */
     398             :     GNUNET_PQ_make_try_execute("CREATE INDEX aggregation_tracking_wtid_index "
     399             :                                "ON aggregation_tracking(wtid_raw)"),
     400             :     /* Table for the wire fees. */
     401             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS wire_fee "
     402             :                            "(wire_method VARCHAR NOT NULL"
     403             :                            ",start_date INT8 NOT NULL"
     404             :                            ",end_date INT8 NOT NULL"
     405             :                            ",wire_fee_val INT8 NOT NULL"
     406             :                            ",wire_fee_frac INT4 NOT NULL"
     407             :                            ",wire_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     408             :                            ",master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)"
     409             :                            ",PRIMARY KEY (wire_method, start_date)" /* this combo must be unique */
     410             :                            ");"),
     411             :     /* Index for lookup_transactions statement on wtid */
     412             :     GNUNET_PQ_make_try_execute("CREATE INDEX aggregation_tracking_wtid_index "
     413             :                                "ON aggregation_tracking(wtid_raw);"),
     414             :     /* Index for gc_wire_fee */
     415             :     GNUNET_PQ_make_try_execute("CREATE INDEX wire_fee_gc_index "
     416             :                                "ON wire_fee(end_date);"),
     417             :     /* Table for /payback information */
     418             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS payback "
     419             :                            "(payback_uuid BIGSERIAL UNIQUE"
     420             :                            ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub)" /* do NOT CASCADE on delete, we may keep the coin alive! */
     421             :                            ",coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64)"
     422             :                            ",coin_blind BYTEA NOT NULL CHECK(LENGTH(coin_blind)=32)"
     423             :                            ",amount_val INT8 NOT NULL"
     424             :                            ",amount_frac INT4 NOT NULL"
     425             :                            ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
     426             :                            ",timestamp INT8 NOT NULL"
     427             :                            ",h_blind_ev BYTEA NOT NULL REFERENCES reserves_out (h_blind_ev) ON DELETE CASCADE"
     428             :                            ");"),
     429             :     GNUNET_PQ_make_try_execute("CREATE INDEX payback_by_coin_index "
     430             :                                "ON payback(coin_pub);"),
     431             :     GNUNET_PQ_make_try_execute("CREATE INDEX payback_by_reserve_index "
     432             :                                "ON payback(reserve_pub);"),
     433             : 
     434             :     /* This table contains the pre-commit data for
     435             :        wire transfers the exchange is about to execute. */
     436             :     GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS prewire "
     437             :                            "(prewire_uuid BIGSERIAL PRIMARY KEY"
     438             :                            ",type TEXT NOT NULL"
     439             :                            ",finished BOOLEAN NOT NULL DEFAULT false"
     440             :                            ",buf BYTEA NOT NULL"
     441             :                            ");"),
     442             :     /* Index for wire_prepare_data_get and gc_prewire statement */
     443             :     GNUNET_PQ_make_try_execute("CREATE INDEX prepare_iteration_index "
     444             :                                "ON prewire(finished);"),
     445             :     GNUNET_PQ_EXECUTE_STATEMENT_END
     446             :   };
     447             :   PGconn *conn;
     448             :   int ret;
     449             : 
     450          36 :   conn = GNUNET_PQ_connect (pc->connection_cfg_str);
     451          36 :   if (NULL == conn)
     452           0 :     return GNUNET_SYSERR;
     453          36 :   ret = GNUNET_PQ_exec_statements (conn,
     454             :                                    es);
     455          36 :   PQfinish (conn);
     456          36 :   return ret;
     457             : }
     458             : 
     459             : 
     460             : /**
     461             :  * Setup prepared statements.
     462             :  *
     463             :  * @param db_conn connection handle to initialize
     464             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
     465             :  */
     466             : static int
     467          38 : postgres_prepare (PGconn *db_conn)
     468             : {
     469          38 :   struct GNUNET_PQ_PreparedStatement ps[] = {
     470             :     /* Used in #postgres_insert_denomination_info() */
     471             :     GNUNET_PQ_make_prepare ("denomination_insert",
     472             :                             "INSERT INTO denominations "
     473             :                             "(denom_pub_hash"
     474             :                             ",denom_pub"
     475             :                             ",master_pub"
     476             :                             ",master_sig"
     477             :                             ",valid_from"
     478             :                             ",expire_withdraw"
     479             :                             ",expire_deposit"
     480             :                             ",expire_legal"
     481             :                             ",coin_val" /* value of this denom */
     482             :                             ",coin_frac" /* fractional value of this denom */
     483             :                             ",coin_curr" /* assuming same currency for fees */
     484             :                             ",fee_withdraw_val"
     485             :                             ",fee_withdraw_frac"
     486             :                             ",fee_withdraw_curr" /* must match coin_curr */
     487             :                             ",fee_deposit_val"
     488             :                             ",fee_deposit_frac"
     489             :                             ",fee_deposit_curr"  /* must match coin_curr */
     490             :                             ",fee_refresh_val"
     491             :                             ",fee_refresh_frac"
     492             :                             ",fee_refresh_curr" /* must match coin_curr */
     493             :                             ",fee_refund_val"
     494             :                             ",fee_refund_frac"
     495             :                             ",fee_refund_curr" /* must match coin_curr */
     496             :                             ") VALUES "
     497             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
     498             :                             " $11, $12, $13, $14, $15, $16, $17, $18,"
     499             :                             " $19, $20, $21, $22, $23);",
     500             :                             23),
     501             :     /* Used in #postgres_get_denomination_info() */
     502             :     GNUNET_PQ_make_prepare ("denomination_get",
     503             :                             "SELECT"
     504             :                             " master_pub"
     505             :                             ",master_sig"
     506             :                             ",valid_from"
     507             :                             ",expire_withdraw"
     508             :                             ",expire_deposit"
     509             :                             ",expire_legal"
     510             :                             ",coin_val"  /* value of this denom */
     511             :                             ",coin_frac" /* fractional value of this denom */
     512             :                             ",coin_curr" /* assuming same currency for fees */
     513             :                             ",fee_withdraw_val"
     514             :                             ",fee_withdraw_frac"
     515             :                             ",fee_withdraw_curr" /* must match coin_curr */
     516             :                             ",fee_deposit_val"
     517             :                             ",fee_deposit_frac"
     518             :                             ",fee_deposit_curr"  /* must match coin_curr */
     519             :                             ",fee_refresh_val"
     520             :                             ",fee_refresh_frac"
     521             :                             ",fee_refresh_curr" /* must match coin_curr */
     522             :                             ",fee_refund_val"
     523             :                             ",fee_refund_frac"
     524             :                             ",fee_refund_curr" /* must match coin_curr */
     525             :                             " FROM denominations"
     526             :                             " WHERE denom_pub_hash=$1;",
     527             :                             1),
     528             :     /* Used in #postgres_insert_denomination_revocation() */
     529             :     GNUNET_PQ_make_prepare ("denomination_revocation_insert",
     530             :                             "INSERT INTO denomination_revocations "
     531             :                             "(denom_pub_hash"
     532             :                             ",master_sig"
     533             :                             ") VALUES "
     534             :                             "($1, $2);",
     535             :                             2),
     536             :     /* Used in #postgres_get_denomination_revocation() */
     537             :     GNUNET_PQ_make_prepare ("denomination_revocation_get",
     538             :                             "SELECT"
     539             :                             " master_sig"
     540             :                             ",denom_revocations_serial_id"
     541             :                             " FROM denomination_revocations"
     542             :                             " WHERE denom_pub_hash=$1;",
     543             :                             1),
     544             :     /* Used in #postgres_reserve_get() */
     545             :     GNUNET_PQ_make_prepare ("reserve_get",
     546             :                             "SELECT"
     547             :                             " current_balance_val"
     548             :                             ",current_balance_frac"
     549             :                             ",current_balance_curr"
     550             :                             ",expiration_date"
     551             :                             " FROM reserves"
     552             :                             " WHERE reserve_pub=$1"
     553             :                             " LIMIT 1;",
     554             :                             1),
     555             :     /* Used in #postgres_reserves_in_insert() when the reserve is new */
     556             :     GNUNET_PQ_make_prepare ("reserve_create",
     557             :                             "INSERT INTO reserves "
     558             :                             "(reserve_pub"
     559             :                             ",account_details"
     560             :                             ",current_balance_val"
     561             :                             ",current_balance_frac"
     562             :                             ",current_balance_curr"
     563             :                             ",expiration_date"
     564             :                             ") VALUES "
     565             :                             "($1, $2, $3, $4, $5, $6);",
     566             :                             6),
     567             :     /* Used in #postgres_insert_reserve_closed() */
     568             :     GNUNET_PQ_make_prepare ("reserves_close_insert",
     569             :                             "INSERT INTO reserves_close "
     570             :                             "(reserve_pub"
     571             :                             ",execution_date"
     572             :                             ",wtid"
     573             :                             ",receiver_account"
     574             :                             ",amount_val"
     575             :                             ",amount_frac"
     576             :                             ",amount_curr"
     577             :                             ",closing_fee_val"
     578             :                             ",closing_fee_frac"
     579             :                             ",closing_fee_curr"
     580             :                             ") VALUES "
     581             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);",
     582             :                             10),
     583             :     /* Used in #postgres_reserves_update() when the reserve is updated */
     584             :     GNUNET_PQ_make_prepare ("reserve_update",
     585             :                             "UPDATE reserves"
     586             :                             " SET"
     587             :                             " expiration_date=$1 "
     588             :                             ",current_balance_val=$2"
     589             :                             ",current_balance_frac=$3"
     590             :                             ",current_balance_curr=$4"
     591             :                             " WHERE"
     592             :                             " reserve_pub=$5;",
     593             :                             5),
     594             :     /* Used in #postgres_reserves_in_insert() to store transaction details */
     595             :     GNUNET_PQ_make_prepare ("reserves_in_add_transaction",
     596             :                             "INSERT INTO reserves_in "
     597             :                             "(reserve_pub"
     598             :                             ",wire_reference"
     599             :                             ",credit_val"
     600             :                             ",credit_frac"
     601             :                             ",credit_curr"
     602             :                             ",sender_account_details"
     603             :                             ",execution_date"
     604             :                             ") VALUES "
     605             :                             "($1, $2, $3, $4, $5, $6, $7);",
     606             :                             7),
     607             :     /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
     608             :        transactions for reserves with serial id '\geq' the given parameter */
     609             :     GNUNET_PQ_make_prepare ("reserves_in_get_latest_wire_reference",
     610             :                             "SELECT"
     611             :                             " wire_reference"
     612             :                             " FROM reserves_in"
     613             :                             " ORDER BY reserve_in_serial_id DESC"
     614             :                             " LIMIT 1;",
     615             :                             0),
     616             :     /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
     617             :        transactions for reserves with serial id '\geq' the given parameter */
     618             :     GNUNET_PQ_make_prepare ("audit_reserves_in_get_transactions_incr",
     619             :                             "SELECT"
     620             :                             " reserve_pub"
     621             :                             ",wire_reference"
     622             :                             ",credit_val"
     623             :                             ",credit_frac"
     624             :                             ",credit_curr"
     625             :                             ",execution_date"
     626             :                             ",sender_account_details"
     627             :                             ",reserve_in_serial_id"
     628             :                             " FROM reserves_in"
     629             :                             " WHERE reserve_in_serial_id>=$1"
     630             :                             " ORDER BY reserve_in_serial_id;",
     631             :                             1),
     632             :     /* Used in #postgres_get_reserve_history() to obtain inbound transactions
     633             :        for a reserve */
     634             :     GNUNET_PQ_make_prepare ("reserves_in_get_transactions",
     635             :                             "SELECT"
     636             :                             " wire_reference"
     637             :                             ",credit_val"
     638             :                             ",credit_frac"
     639             :                             ",credit_curr"
     640             :                             ",execution_date"
     641             :                             ",sender_account_details"
     642             :                             " FROM reserves_in"
     643             :                             " WHERE reserve_pub=$1;",
     644             :                             1),
     645             :     /* Used in #postgres_insert_withdraw_info() to store
     646             :        the signature of a blinded coin with the blinded coin's
     647             :        details before returning it during /reserve/withdraw. We store
     648             :        the coin's denomination information (public key, signature)
     649             :        and the blinded message as well as the reserve that the coin
     650             :        is being withdrawn from and the signature of the message
     651             :        authorizing the withdrawal. */
     652             :     GNUNET_PQ_make_prepare ("insert_withdraw_info",
     653             :                             "INSERT INTO reserves_out "
     654             :                             "(h_blind_ev"
     655             :                             ",denom_pub_hash"
     656             :                             ",denom_sig"
     657             :                             ",reserve_pub"
     658             :                             ",reserve_sig"
     659             :                             ",execution_date"
     660             :                             ",amount_with_fee_val"
     661             :                             ",amount_with_fee_frac"
     662             :                             ",amount_with_fee_curr"
     663             :                             ") VALUES "
     664             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
     665             :                             9),
     666             :     /* Used in #postgres_get_withdraw_info() to
     667             :        locate the response for a /reserve/withdraw request
     668             :        using the hash of the blinded message.  Used to
     669             :        make sure /reserve/withdraw requests are idempotent. */
     670             :     GNUNET_PQ_make_prepare ("get_withdraw_info",
     671             :                             "SELECT"
     672             :                             " denom.denom_pub"
     673             :                             ",denom_sig"
     674             :                             ",reserve_sig"
     675             :                             ",reserve_pub"
     676             :                             ",execution_date"
     677             :                             ",amount_with_fee_val"
     678             :                             ",amount_with_fee_frac"
     679             :                             ",amount_with_fee_curr"
     680             :                             ",denom.fee_withdraw_val"
     681             :                             ",denom.fee_withdraw_frac"
     682             :                             ",denom.fee_withdraw_curr"
     683             :                             " FROM reserves_out"
     684             :                             "    JOIN denominations denom"
     685             :                             "      USING (denom_pub_hash)"
     686             :                             " WHERE h_blind_ev=$1;",
     687             :                             1),
     688             :     /* Used during #postgres_get_reserve_history() to
     689             :        obtain all of the /reserve/withdraw operations that
     690             :        have been performed on a given reserve. (i.e. to
     691             :        demonstrate double-spending) */
     692             :     GNUNET_PQ_make_prepare ("get_reserves_out",
     693             :                             "SELECT"
     694             :                             " h_blind_ev"
     695             :                             ",denom.denom_pub"
     696             :                             ",denom_sig"
     697             :                             ",reserve_sig"
     698             :                             ",execution_date"
     699             :                             ",amount_with_fee_val"
     700             :                             ",amount_with_fee_frac"
     701             :                             ",amount_with_fee_curr"
     702             :                             ",denom.fee_withdraw_val"
     703             :                             ",denom.fee_withdraw_frac"
     704             :                             ",denom.fee_withdraw_curr"
     705             :                             " FROM reserves_out"
     706             :                             "    JOIN denominations denom"
     707             :                             "      USING (denom_pub_hash)"
     708             :                             " WHERE reserve_pub=$1;",
     709             :                             1),
     710             :     /* Used in #postgres_select_reserves_out_above_serial_id() */
     711             :     GNUNET_PQ_make_prepare ("audit_get_reserves_out_incr",
     712             :                             "SELECT"
     713             :                             " h_blind_ev"
     714             :                             ",denom.denom_pub"
     715             :                             ",denom_sig"
     716             :                             ",reserve_sig"
     717             :                             ",reserve_pub"
     718             :                             ",execution_date"
     719             :                             ",amount_with_fee_val"
     720             :                             ",amount_with_fee_frac"
     721             :                             ",amount_with_fee_curr"
     722             :                             ",reserve_out_serial_id"
     723             :                             " FROM reserves_out"
     724             :                             "    JOIN denominations denom"
     725             :                             "      USING (denom_pub_hash)"
     726             :                             " WHERE reserve_out_serial_id>=$1"
     727             :                             " ORDER BY reserve_out_serial_id ASC;",
     728             :                             1),
     729             :     /* Used in #postgres_get_refresh_session() to fetch
     730             :        high-level information about a refresh session */
     731             :     GNUNET_PQ_make_prepare ("get_refresh_session",
     732             :                             "SELECT"
     733             :                             " old_coin_pub"
     734             :                             ",old_coin_sig"
     735             :                             ",amount_with_fee_val"
     736             :                             ",amount_with_fee_frac"
     737             :                             ",amount_with_fee_curr"
     738             :                             ",denom.fee_refresh_val "
     739             :                             ",denom.fee_refresh_frac "
     740             :                             ",denom.fee_refresh_curr "
     741             :                             ",num_newcoins"
     742             :                             ",noreveal_index"
     743             :                             " FROM refresh_sessions"
     744             :                             "    JOIN known_coins"
     745             :                             "      ON (refresh_sessions.old_coin_pub = known_coins.coin_pub)"
     746             :                             "    JOIN denominations denom"
     747             :                             "      USING (denom_pub_hash)"
     748             :                             " WHERE session_hash=$1;",
     749             :                             1),
     750             :     /* Used in #postgres_select_refreshs_above_serial_id() to fetch
     751             :        refresh session with id '\geq' the given parameter */
     752             :     GNUNET_PQ_make_prepare ("audit_get_refresh_sessions_incr",
     753             :                             "SELECT"
     754             :                             " denom.denom_pub"
     755             :                             ",old_coin_pub"
     756             :                             ",old_coin_sig"
     757             :                             ",amount_with_fee_val"
     758             :                             ",amount_with_fee_frac"
     759             :                             ",amount_with_fee_curr"
     760             :                             ",num_newcoins"
     761             :                             ",noreveal_index"
     762             :                             ",melt_serial_id"
     763             :                             ",session_hash"
     764             :                             " FROM refresh_sessions"
     765             :                             "   JOIN known_coins kc"
     766             :                             "     ON (refresh_sessions.old_coin_pub = kc.coin_pub)"
     767             :                             "   JOIN denominations denom"
     768             :                             "     ON (kc.denom_pub_hash = denom.denom_pub_hash)"
     769             :                             " WHERE melt_serial_id>=$1"
     770             :                             " ORDER BY melt_serial_id ASC;",
     771             :                             1),
     772             :     /* Used in #postgres_create_refresh_session() to store
     773             :        high-level information about a refresh session */
     774             :     GNUNET_PQ_make_prepare ("insert_refresh_session",
     775             :                             "INSERT INTO refresh_sessions "
     776             :                             "(session_hash "
     777             :                             ",old_coin_pub "
     778             :                             ",old_coin_sig "
     779             :                             ",amount_with_fee_val "
     780             :                             ",amount_with_fee_frac "
     781             :                             ",amount_with_fee_curr "
     782             :                             ",num_newcoins "
     783             :                             ",noreveal_index "
     784             :                             ") VALUES "
     785             :                             "($1, $2, $3, $4, $5, $6, $7, $8);",
     786             :                             8),
     787             :     /* Used in #postgres_get_known_coin() to fetch
     788             :        the denomination public key and signature for
     789             :        a coin known to the exchange. */
     790             :     GNUNET_PQ_make_prepare ("get_known_coin",
     791             :                             "SELECT"
     792             :                             " denom.denom_pub"
     793             :                             ",denom_sig"
     794             :                             " FROM known_coins"
     795             :                             "    JOIN denominations denom"
     796             :                             "      USING (denom_pub_hash)"
     797             :                             " WHERE coin_pub=$1;",
     798             :                             1),
     799             :     /* Used in #postgres_insert_known_coin() to store
     800             :        the denomination public key and signature for
     801             :        a coin known to the exchange. */
     802             :     GNUNET_PQ_make_prepare ("insert_known_coin",
     803             :                             "INSERT INTO known_coins "
     804             :                             "(coin_pub"
     805             :                             ",denom_pub_hash"
     806             :                             ",denom_sig"
     807             :                             ") VALUES "
     808             :                             "($1,$2,$3);",
     809             :                             3),
     810             :     /* Store information about the desired denominations for a
     811             :        refresh operation, used in #postgres_insert_refresh_order() */
     812             :     GNUNET_PQ_make_prepare ("insert_refresh_order",
     813             :                             "INSERT INTO refresh_order "
     814             :                             "(newcoin_index "
     815             :                             ",session_hash "
     816             :                             ",denom_pub_hash "
     817             :                             ") VALUES "
     818             :                             "($1, $2, $3);",
     819             :                             3),
     820             :     /* Obtain information about the desired denominations for a
     821             :        refresh operation, used in #postgres_get_refresh_order() */
     822             :     GNUNET_PQ_make_prepare ("get_refresh_order",
     823             :                             "SELECT denom_pub"
     824             :                             " FROM refresh_order"
     825             :                             "    JOIN denominations denom "
     826             :                             "      USING (denom_pub_hash)"
     827             :                             " WHERE session_hash=$1"
     828             :                             "   AND newcoin_index=$2;",
     829             :                             2),
     830             :     /* Query the 'refresh_sessions' by coin public key */
     831             :     GNUNET_PQ_make_prepare ("get_refresh_session_by_coin",
     832             :                             "SELECT"
     833             :                             " session_hash"
     834             :                             ",old_coin_sig"
     835             :                             ",amount_with_fee_val"
     836             :                             ",amount_with_fee_frac"
     837             :                             ",amount_with_fee_curr"
     838             :                             ",denom.fee_refresh_val "
     839             :                             ",denom.fee_refresh_frac "
     840             :                             ",denom.fee_refresh_curr "
     841             :                             " FROM refresh_sessions"
     842             :                             "    JOIN known_coins "
     843             :                             "      ON (refresh_sessions.old_coin_pub = known_coins.coin_pub)"
     844             :                             "    JOIN denominations denom USING (denom_pub_hash)"
     845             :                             " WHERE old_coin_pub=$1;",
     846             :                             1),
     847             :     /* Fetch refunds with rowid '\geq' the given parameter */
     848             :     GNUNET_PQ_make_prepare ("audit_get_refunds_incr",
     849             :                             "SELECT"
     850             :                             " merchant_pub"
     851             :                             ",merchant_sig"
     852             :                             ",h_contract_terms"
     853             :                             ",rtransaction_id"
     854             :                             ",denom.denom_pub"
     855             :                             ",coin_pub"
     856             :                             ",amount_with_fee_val"
     857             :                             ",amount_with_fee_frac"
     858             :                             ",amount_with_fee_curr"
     859             :                             ",refund_serial_id"
     860             :                             " FROM refunds"
     861             :                             "   JOIN known_coins kc USING (coin_pub)"
     862             :                             "   JOIN denominations denom ON (kc.denom_pub_hash = denom.denom_pub_hash)"
     863             :                             " WHERE refund_serial_id>=$1"
     864             :                             " ORDER BY refund_serial_id ASC;",
     865             :                             1),
     866             :     /* Query the 'refunds' by coin public key */
     867             :     GNUNET_PQ_make_prepare ("get_refunds_by_coin",
     868             :                             "SELECT"
     869             :                             " merchant_pub"
     870             :                             ",merchant_sig"
     871             :                             ",h_contract_terms"
     872             :                             ",rtransaction_id"
     873             :                             ",amount_with_fee_val"
     874             :                             ",amount_with_fee_frac"
     875             :                             ",amount_with_fee_curr"
     876             :                             ",denom.fee_refund_val "
     877             :                             ",denom.fee_refund_frac "
     878             :                             ",denom.fee_refund_curr "
     879             :                             " FROM refunds"
     880             :                             "    JOIN known_coins USING (coin_pub)"
     881             :                             "    JOIN denominations denom USING (denom_pub_hash)"
     882             :                             " WHERE coin_pub=$1;",
     883             :                             1),
     884             :     /* Used in #postgres_insert_transfer_public_key() to
     885             :        store commitments */
     886             :     GNUNET_PQ_make_prepare ("insert_transfer_public_key",
     887             :                             "INSERT INTO refresh_transfer_public_key "
     888             :                             "(session_hash"
     889             :                             ",transfer_pub"
     890             :                             ") VALUES "
     891             :                             "($1, $2);",
     892             :                             2),
     893             :     /* Used in #postgres_get_refresh_transfer_public_key() to
     894             :        retrieve original commitments during /refresh/reveal */
     895             :     GNUNET_PQ_make_prepare ("get_refresh_transfer_public_key",
     896             :                             "SELECT"
     897             :                             " transfer_pub"
     898             :                             " FROM refresh_transfer_public_key"
     899             :                             " WHERE session_hash=$1;",
     900             :                             1),
     901             :     /* Used in #postgres_insert_refresh_commit_coins() to
     902             :        store coin commitments. */
     903             :     GNUNET_PQ_make_prepare ("insert_refresh_commit_coin",
     904             :                             "INSERT INTO refresh_commit_coin "
     905             :                             "(session_hash"
     906             :                             ",newcoin_index"
     907             :                             ",coin_ev"
     908             :                             ") VALUES "
     909             :                             "($1, $2, $3);",
     910             :                             3),
     911             :     /* Used in #postgres_get_refresh_commit_coins() to
     912             :        retrieve the original coin envelopes, to either be
     913             :        verified or signed. */
     914             :     GNUNET_PQ_make_prepare ("get_refresh_commit_coin",
     915             :                             "SELECT"
     916             :                             " coin_ev"
     917             :                             " FROM refresh_commit_coin"
     918             :                             " WHERE session_hash=$1"
     919             :                             " AND newcoin_index=$2;",
     920             :                             2),
     921             :     /* Store information about a /deposit the exchange is to execute.
     922             :        Used in #postgres_insert_deposit(). */
     923             :     GNUNET_PQ_make_prepare ("insert_deposit",
     924             :                             "INSERT INTO deposits "
     925             :                             "(coin_pub"
     926             :                             ",amount_with_fee_val"
     927             :                             ",amount_with_fee_frac"
     928             :                             ",amount_with_fee_curr"
     929             :                             ",timestamp"
     930             :                             ",refund_deadline"
     931             :                             ",wire_deadline"
     932             :                             ",merchant_pub"
     933             :                             ",h_contract_terms"
     934             :                             ",h_wire"
     935             :                             ",coin_sig"
     936             :                             ",wire"
     937             :                             ") VALUES "
     938             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
     939             :                             " $11, $12);",
     940             :                             12),
     941             :     /* Used in #postgres_insert_refund() to store refund information */
     942             :     GNUNET_PQ_make_prepare ("insert_refund",
     943             :                             "INSERT INTO refunds "
     944             :                             "(coin_pub "
     945             :                             ",merchant_pub "
     946             :                             ",merchant_sig "
     947             :                             ",h_contract_terms "
     948             :                             ",rtransaction_id "
     949             :                             ",amount_with_fee_val "
     950             :                             ",amount_with_fee_frac "
     951             :                             ",amount_with_fee_curr "
     952             :                             ") VALUES "
     953             :                             "($1, $2, $3, $4, $5, $6, $7, $8);",
     954             :                             8),
     955             :     /* Fetch an existing deposit request, used to ensure idempotency
     956             :        during /deposit processing. Used in #postgres_have_deposit(). */
     957             :     GNUNET_PQ_make_prepare ("get_deposit",
     958             :                             "SELECT"
     959             :                             " amount_with_fee_val"
     960             :                             ",amount_with_fee_frac"
     961             :                             ",amount_with_fee_curr"
     962             :                             ",timestamp"
     963             :                             ",refund_deadline"
     964             :                             ",wire_deadline"
     965             :                             ",h_contract_terms"
     966             :                             ",h_wire"
     967             :                             " FROM deposits"
     968             :                             " WHERE ("
     969             :                             "        (coin_pub=$1)"
     970             :                             "    AND (h_contract_terms=$2)"
     971             :                             "    AND (merchant_pub=$3)"
     972             :                             " );",
     973             :                             3),
     974             :     /* Fetch deposits with rowid '\geq' the given parameter */
     975             :     GNUNET_PQ_make_prepare ("audit_get_deposits_incr",
     976             :                             "SELECT"
     977             :                             " amount_with_fee_val"
     978             :                             ",amount_with_fee_frac"
     979             :                             ",amount_with_fee_curr"
     980             :                             ",timestamp"
     981             :                             ",merchant_pub"
     982             :                             ",denom.denom_pub"
     983             :                             ",coin_pub"
     984             :                             ",coin_sig"
     985             :                             ",refund_deadline"
     986             :                             ",wire_deadline"
     987             :                             ",h_contract_terms"
     988             :                             ",wire"
     989             :                             ",done"
     990             :                             ",deposit_serial_id"
     991             :                             " FROM deposits"
     992             :                             "    JOIN known_coins USING (coin_pub)"
     993             :                             "    JOIN denominations denom USING (denom_pub_hash)"
     994             :                             " WHERE ("
     995             :                             "  (deposit_serial_id>=$1)"
     996             :                             " )"
     997             :                             " ORDER BY deposit_serial_id ASC;",
     998             :                             1),
     999             :     /* Fetch an existing deposit request.
    1000             :        Used in #postgres_wire_lookup_deposit_wtid(). */
    1001             :     GNUNET_PQ_make_prepare ("get_deposit_for_wtid",
    1002             :                             "SELECT"
    1003             :                             " amount_with_fee_val"
    1004             :                             ",amount_with_fee_frac"
    1005             :                             ",amount_with_fee_curr"
    1006             :                             ",denom.fee_deposit_val"
    1007             :                             ",denom.fee_deposit_frac"
    1008             :                             ",denom.fee_deposit_curr"
    1009             :                             ",wire_deadline"
    1010             :                             " FROM deposits"
    1011             :                             "    JOIN known_coins USING (coin_pub)"
    1012             :                             "    JOIN denominations denom USING (denom_pub_hash)"
    1013             :                             " WHERE ("
    1014             :                             "      (coin_pub=$1)"
    1015             :                             "    AND (merchant_pub=$2)"
    1016             :                             "    AND (h_contract_terms=$3)"
    1017             :                             "    AND (h_wire=$4)"
    1018             :                             " );",
    1019             :                             4),
    1020             :     /* Used in #postgres_get_ready_deposit() */
    1021             :     GNUNET_PQ_make_prepare ("deposits_get_ready",
    1022             :                             "SELECT"
    1023             :                             " deposit_serial_id"
    1024             :                             ",amount_with_fee_val"
    1025             :                             ",amount_with_fee_frac"
    1026             :                             ",amount_with_fee_curr"
    1027             :                             ",denom.fee_deposit_val"
    1028             :                             ",denom.fee_deposit_frac"
    1029             :                             ",denom.fee_deposit_curr"
    1030             :                             ",wire_deadline"
    1031             :                             ",h_contract_terms"
    1032             :                             ",wire"
    1033             :                             ",merchant_pub"
    1034             :                             ",coin_pub"
    1035             :                             " FROM deposits"
    1036             :                             "    JOIN known_coins USING (coin_pub)"
    1037             :                             "    JOIN denominations denom USING (denom_pub_hash)"
    1038             :                             " WHERE tiny=false"
    1039             :                             "    AND done=false"
    1040             :                             "    AND wire_deadline<=$1"
    1041             :                             "    AND refund_deadline<$1"
    1042             :                             " ORDER BY wire_deadline ASC"
    1043             :                             " LIMIT 1;",
    1044             :                             1),
    1045             :     /* Used in #postgres_iterate_matching_deposits() */
    1046             :     GNUNET_PQ_make_prepare ("deposits_iterate_matching",
    1047             :                             "SELECT"
    1048             :                             " deposit_serial_id"
    1049             :                             ",amount_with_fee_val"
    1050             :                             ",amount_with_fee_frac"
    1051             :                             ",amount_with_fee_curr"
    1052             :                             ",denom.fee_deposit_val"
    1053             :                             ",denom.fee_deposit_frac"
    1054             :                             ",denom.fee_deposit_curr"
    1055             :                             ",wire_deadline"
    1056             :                             ",h_contract_terms"
    1057             :                             ",coin_pub"
    1058             :                             " FROM deposits"
    1059             :                             "    JOIN known_coins"
    1060             :                             "      USING (coin_pub)"
    1061             :                             "    JOIN denominations denom"
    1062             :                             "      USING (denom_pub_hash)"
    1063             :                             " WHERE"
    1064             :                             " merchant_pub=$1 AND"
    1065             :                             " h_wire=$2 AND"
    1066             :                             " done=false"
    1067             :                             " ORDER BY wire_deadline ASC"
    1068             :                             " LIMIT " TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT_STR ";",
    1069             :                             2),
    1070             :     /* Used in #postgres_mark_deposit_tiny() */
    1071             :     GNUNET_PQ_make_prepare ("mark_deposit_tiny",
    1072             :                             "UPDATE deposits"
    1073             :                             " SET tiny=true"
    1074             :                             " WHERE deposit_serial_id=$1",
    1075             :                             1),
    1076             :     /* Used in #postgres_mark_deposit_done() */
    1077             :     GNUNET_PQ_make_prepare ("mark_deposit_done",
    1078             :                             "UPDATE deposits"
    1079             :                             " SET done=true"
    1080             :                             " WHERE deposit_serial_id=$1;",
    1081             :                             1),
    1082             :     /* Used in #postgres_test_deposit_done() */
    1083             :     GNUNET_PQ_make_prepare ("test_deposit_done",
    1084             :                             "SELECT done"
    1085             :                             " FROM deposits"
    1086             :                             " WHERE coin_pub=$1"
    1087             :                             "   AND merchant_pub=$2"
    1088             :                             "   AND h_contract_terms=$3"
    1089             :                             "   AND h_wire=$4;",
    1090             :                             5),
    1091             :     /* Used in #postgres_get_coin_transactions() to obtain information
    1092             :        about how a coin has been spend with /deposit requests. */
    1093             :     GNUNET_PQ_make_prepare ("get_deposit_with_coin_pub",
    1094             :                             "SELECT"
    1095             :                             " amount_with_fee_val"
    1096             :                             ",amount_with_fee_frac"
    1097             :                             ",amount_with_fee_curr"
    1098             :                             ",denom.fee_deposit_val"
    1099             :                             ",denom.fee_deposit_frac"
    1100             :                             ",denom.fee_deposit_curr"
    1101             :                             ",timestamp"
    1102             :                             ",refund_deadline"
    1103             :                             ",merchant_pub"
    1104             :                             ",h_contract_terms"
    1105             :                             ",h_wire"
    1106             :                             ",wire"
    1107             :                             ",coin_sig"
    1108             :                             " FROM deposits"
    1109             :                             "    JOIN known_coins"
    1110             :                             "      USING (coin_pub)"
    1111             :                             "    JOIN denominations denom"
    1112             :                             "      USING (denom_pub_hash)"
    1113             :                             " WHERE coin_pub=$1;",
    1114             :                             1),
    1115             :     /* Used in #postgres_insert_refresh_out() to store the
    1116             :        generated signature(s) for future requests, i.e. /refresh/link */
    1117             :     GNUNET_PQ_make_prepare ("insert_refresh_out",
    1118             :                             "INSERT INTO refresh_out "
    1119             :                             "(session_hash"
    1120             :                             ",newcoin_index"
    1121             :                             ",ev_sig"
    1122             :                             ") VALUES "
    1123             :                             "($1, $2, $3);",
    1124             :                             3),
    1125             :     /* Used in #postgres_get_refresh_out() to test if the
    1126             :        generated signature(s) already exists */
    1127             :     GNUNET_PQ_make_prepare ("get_refresh_out",
    1128             :                             "SELECT ev_sig"
    1129             :                             " FROM refresh_out"
    1130             :                             " WHERE session_hash=$1"
    1131             :                             " AND newcoin_index=$2;",
    1132             :                             2),
    1133             :     /* Used in #postgres_get_link_data_list().  We use the session_hash
    1134             :        to obtain the "noreveal_index" for that session, and then select the
    1135             :        corresponding signatures (ev_sig) and the denomination keys from
    1136             :        the respective tables (namely refresh_melts and refresh_order)
    1137             :        using the session_hash as the primary filter (on join) and the
    1138             :        'noreveal_index' to constrain the selection on the commitment.
    1139             :        We also want to get the triplet for each of the newcoins, so we
    1140             :        have another constraint to ensure we get each triplet with
    1141             :        matching "newcoin_index" values.  NOTE: This may return many
    1142             :        results, both for different sessions and for the different coins
    1143             :        being exchangeed in the refresh ops.  NOTE: There may be more
    1144             :        efficient ways to express the same query.  */
    1145             :     GNUNET_PQ_make_prepare ("get_link",
    1146             :                             "SELECT "
    1147             :                             " ev_sig"
    1148             :                             ",denoms.denom_pub"
    1149             :                             " FROM refresh_sessions"
    1150             :                             "     JOIN refresh_order ro"
    1151             :                             "       USING (session_hash)"
    1152             :                             "     JOIN refresh_commit_coin rcc"
    1153             :                             "       USING (session_hash)"
    1154             :                             "     JOIN refresh_out rc"
    1155             :                             "       USING (session_hash)"
    1156             :                             "     JOIN denominations denoms"
    1157             :                             "       ON (ro.denom_pub_hash = denoms.denom_pub_hash)"
    1158             :                             " WHERE ro.session_hash=$1"
    1159             :                             "  AND ro.newcoin_index=rcc.newcoin_index"
    1160             :                             "  AND ro.newcoin_index=rc.newcoin_index;",
    1161             :                             1),
    1162             :     /* Used in #postgres_get_transfer().  Given the public key of a
    1163             :        melted coin, we obtain the corresponding encrypted link secret
    1164             :        and the transfer public key.  This is done by first finding
    1165             :        the session_hash(es) of all sessions the coin was melted into,
    1166             :        and then constraining the result to the selected "noreveal_index".
    1167             :        NOTE: This may (in theory) return multiple results, one per session
    1168             :        that the old coin was melted into. */
    1169             :     GNUNET_PQ_make_prepare ("get_transfer",
    1170             :                             "SELECT"
    1171             :                             " transfer_pub"
    1172             :                             ",session_hash"
    1173             :                             " FROM refresh_sessions rs"
    1174             :                             "     JOIN refresh_transfer_public_key rcl"
    1175             :                             "       USING (session_hash)"
    1176             :                             " WHERE rs.old_coin_pub=$1;",
    1177             :                             1),
    1178             :     /* Used in #postgres_lookup_wire_transfer */
    1179             :     GNUNET_PQ_make_prepare ("lookup_transactions",
    1180             :                             "SELECT"
    1181             :                             " aggregation_serial_id"
    1182             :                             ",deposits.h_contract_terms"
    1183             :                             ",deposits.wire"
    1184             :                             ",deposits.h_wire"
    1185             :                             ",deposits.coin_pub"
    1186             :                             ",deposits.merchant_pub"
    1187             :                             ",wire_out.execution_date"
    1188             :                             ",deposits.amount_with_fee_val"
    1189             :                             ",deposits.amount_with_fee_frac"
    1190             :                             ",deposits.amount_with_fee_curr"
    1191             :                             ",denom.fee_deposit_val"
    1192             :                             ",denom.fee_deposit_frac"
    1193             :                             ",denom.fee_deposit_curr"
    1194             :                             " FROM aggregation_tracking"
    1195             :                             "    JOIN deposits"
    1196             :                             "      USING (deposit_serial_id)"
    1197             :                             "    JOIN known_coins"
    1198             :                             "      USING (coin_pub)"
    1199             :                             "    JOIN denominations denom"
    1200             :                             "      USING (denom_pub_hash)"
    1201             :                             "    JOIN wire_out"
    1202             :                             "      USING (wtid_raw)"
    1203             :                             " WHERE wtid_raw=$1;",
    1204             :                             1),
    1205             :     /* Used in #postgres_wire_lookup_deposit_wtid */
    1206             :     GNUNET_PQ_make_prepare ("lookup_deposit_wtid",
    1207             :                             "SELECT"
    1208             :                             " aggregation_tracking.wtid_raw"
    1209             :                             ",wire_out.execution_date"
    1210             :                             ",amount_with_fee_val"
    1211             :                             ",amount_with_fee_frac"
    1212             :                             ",amount_with_fee_curr"
    1213             :                             ",denom.fee_deposit_val"
    1214             :                             ",denom.fee_deposit_frac"
    1215             :                             ",denom.fee_deposit_curr"
    1216             :                             " FROM deposits"
    1217             :                             "    JOIN aggregation_tracking"
    1218             :                             "      USING (deposit_serial_id)"
    1219             :                             "    JOIN known_coins"
    1220             :                             "      USING (coin_pub)"
    1221             :                             "    JOIN denominations denom"
    1222             :                             "      USING (denom_pub_hash)"
    1223             :                             "    JOIN wire_out"
    1224             :                             "      USING (wtid_raw)"
    1225             :                             " WHERE coin_pub=$1"
    1226             :                             "  AND h_contract_terms=$2"
    1227             :                             "  AND h_wire=$3"
    1228             :                             "  AND merchant_pub=$4;",
    1229             :                             4),
    1230             :     /* Used in #postgres_insert_aggregation_tracking */
    1231             :     GNUNET_PQ_make_prepare ("insert_aggregation_tracking",
    1232             :                             "INSERT INTO aggregation_tracking "
    1233             :                             "(deposit_serial_id"
    1234             :                             ",wtid_raw"
    1235             :                             ") VALUES "
    1236             :                             "($1, $2);",
    1237             :                             2),
    1238             :     /* Used in #postgres_get_wire_fee() */
    1239             :     GNUNET_PQ_make_prepare ("get_wire_fee",
    1240             :                             "SELECT "
    1241             :                             " start_date"
    1242             :                             ",end_date"
    1243             :                             ",wire_fee_val"
    1244             :                             ",wire_fee_frac"
    1245             :                             ",wire_fee_curr"
    1246             :                             ",master_sig"
    1247             :                             " FROM wire_fee"
    1248             :                             " WHERE wire_method=$1"
    1249             :                             "   AND start_date <= $2"
    1250             :                             "   AND end_date > $2;",
    1251             :                             2),
    1252             :     /* Used in #postgres_insert_wire_fee */
    1253             :     GNUNET_PQ_make_prepare ("insert_wire_fee",
    1254             :                             "INSERT INTO wire_fee "
    1255             :                             "(wire_method"
    1256             :                             ",start_date"
    1257             :                             ",end_date"
    1258             :                             ",wire_fee_val"
    1259             :                             ",wire_fee_frac"
    1260             :                             ",wire_fee_curr"
    1261             :                             ",master_sig"
    1262             :                             ") VALUES "
    1263             :                             "($1, $2, $3, $4, $5, $6, $7);",
    1264             :                             7),
    1265             :     /* Used in #postgres_store_wire_transfer_out */
    1266             :     GNUNET_PQ_make_prepare ("insert_wire_out",
    1267             :                             "INSERT INTO wire_out "
    1268             :                             "(execution_date"
    1269             :                             ",wtid_raw"
    1270             :                             ",wire_target"
    1271             :                             ",amount_val"
    1272             :                             ",amount_frac"
    1273             :                             ",amount_curr"
    1274             :                             ") VALUES "
    1275             :                             "($1, $2, $3, $4, $5, $6);",
    1276             :                             6),
    1277             :     /* Used in #postgres_wire_prepare_data_insert() to store
    1278             :        wire transfer information before actually committing it with the bank */
    1279             :     GNUNET_PQ_make_prepare ("wire_prepare_data_insert",
    1280             :                             "INSERT INTO prewire "
    1281             :                             "(type"
    1282             :                             ",buf"
    1283             :                             ") VALUES "
    1284             :                             "($1, $2);",
    1285             :                             2),
    1286             :     /* Used in #postgres_wire_prepare_data_mark_finished() */
    1287             :     GNUNET_PQ_make_prepare ("wire_prepare_data_mark_done",
    1288             :                             "UPDATE prewire"
    1289             :                             " SET finished=true"
    1290             :                             " WHERE prewire_uuid=$1;",
    1291             :                             1),
    1292             :     /* Used in #postgres_wire_prepare_data_get() */
    1293             :     GNUNET_PQ_make_prepare ("wire_prepare_data_get",
    1294             :                             "SELECT"
    1295             :                             " prewire_uuid"
    1296             :                             ",type"
    1297             :                             ",buf"
    1298             :                             " FROM prewire"
    1299             :                             " WHERE finished=false"
    1300             :                             " ORDER BY prewire_uuid ASC"
    1301             :                             " LIMIT 1;",
    1302             :                             0),
    1303             :     /* Used in #postgres_gc() */
    1304             :     GNUNET_PQ_make_prepare ("gc_prewire",
    1305             :                             "DELETE"
    1306             :                             " FROM prewire"
    1307             :                             " WHERE finished=true;",
    1308             :                             0),
    1309             :     /* Used in #postgres_select_wire_out_above_serial_id() */
    1310             :     GNUNET_PQ_make_prepare ("audit_get_wire_incr",
    1311             :                             "SELECT"
    1312             :                             " wireout_uuid"
    1313             :                             ",execution_date"
    1314             :                             ",wtid_raw"
    1315             :                             ",wire_target"
    1316             :                             ",amount_val"
    1317             :                             ",amount_frac"
    1318             :                             ",amount_curr"
    1319             :                             " FROM wire_out"
    1320             :                             " WHERE wireout_uuid>=$1"
    1321             :                             " ORDER BY wireout_uuid ASC;",
    1322             :                             1),
    1323             :     /* Used in #postgres_insert_payback_request() to store payback
    1324             :        information */
    1325             :     GNUNET_PQ_make_prepare ("payback_insert",
    1326             :                             "INSERT INTO payback "
    1327             :                             "(coin_pub"
    1328             :                             ",coin_sig"
    1329             :                             ",coin_blind"
    1330             :                             ",amount_val"
    1331             :                             ",amount_frac"
    1332             :                             ",amount_curr"
    1333             :                             ",timestamp"
    1334             :                             ",h_blind_ev"
    1335             :                             ") VALUES "
    1336             :                             "($1, $2, $3, $4, $5, $6, $7, $8);",
    1337             :                             8),
    1338             :     /* Used in #postgres_select_payback_above_serial_id() to obtain payback transactions */
    1339             :     GNUNET_PQ_make_prepare ("payback_get_incr",
    1340             :                             "SELECT"
    1341             :                             " payback_uuid"
    1342             :                             ",timestamp"
    1343             :                             ",ro.reserve_pub"
    1344             :                             ",coin_pub"
    1345             :                             ",coin_sig"
    1346             :                             ",coin_blind"
    1347             :                             ",h_blind_ev"
    1348             :                             ",denoms.denom_pub"
    1349             :                             ",coins.denom_sig"
    1350             :                             ",amount_val"
    1351             :                             ",amount_frac"
    1352             :                             ",amount_curr"
    1353             :                             " FROM payback"
    1354             :                             "    JOIN known_coins coins"
    1355             :                             "      USING (coin_pub)"
    1356             :                             "    JOIN denominations denoms"
    1357             :                             "      USING (denom_pub_hash)"
    1358             :                             "    JOIN reserves_out ro"
    1359             :                             "      USING (h_blind_ev)"
    1360             :                             " WHERE payback_uuid>=$1"
    1361             :                             " ORDER BY payback_uuid ASC;",
    1362             :                             1),
    1363             :     /* Used in #postgres_select_reserve_closed_above_serial_id() to
    1364             :        obtain information about closed reserves */
    1365             :     GNUNET_PQ_make_prepare ("reserves_close_get_incr",
    1366             :                             "SELECT"
    1367             :                             " close_uuid"
    1368             :                             ",reserve_pub"
    1369             :                             ",execution_date"
    1370             :                             ",wtid"
    1371             :                             ",receiver_account"
    1372             :                             ",amount_val"
    1373             :                             ",amount_frac"
    1374             :                             ",amount_curr"
    1375             :                             ",closing_fee_val"
    1376             :                             ",closing_fee_frac"
    1377             :                             ",closing_fee_curr"
    1378             :                             " FROM reserves_close"
    1379             :                             " WHERE close_uuid>=$1"
    1380             :                             " ORDER BY close_uuid ASC;",
    1381             :                             1),
    1382             :     /* Used in #postgres_get_reserve_history() to obtain payback transactions
    1383             :        for a reserve */
    1384             :     GNUNET_PQ_make_prepare ("payback_by_reserve",
    1385             :                             "SELECT"
    1386             :                             " coin_pub"
    1387             :                             ",coin_sig"
    1388             :                             ",coin_blind"
    1389             :                             ",amount_val"
    1390             :                             ",amount_frac"
    1391             :                             ",amount_curr"
    1392             :                             ",timestamp"
    1393             :                             ",denoms.denom_pub"
    1394             :                             ",coins.denom_sig"
    1395             :                             " FROM payback"
    1396             :                             "    JOIN known_coins coins"
    1397             :                             "      USING (coin_pub)"
    1398             :                             "    JOIN denominations denoms"
    1399             :                             "      USING (denom_pub_hash)"
    1400             :                             "    JOIN reserves_out ro"
    1401             :                             "      USING (h_blind_ev)"
    1402             :                             " WHERE ro.reserve_pub=$1;",
    1403             :                             1),
    1404             :     /* Used in #postgres_get_reserve_history() */
    1405             :     GNUNET_PQ_make_prepare ("close_by_reserve",
    1406             :                             "SELECT"
    1407             :                             " amount_val"
    1408             :                             ",amount_frac"
    1409             :                             ",amount_curr"
    1410             :                             ",closing_fee_val"
    1411             :                             ",closing_fee_frac"
    1412             :                             ",closing_fee_curr"
    1413             :                             ",execution_date"
    1414             :                             ",receiver_account"
    1415             :                             ",wtid"
    1416             :                             " FROM reserves_close"
    1417             :                             " WHERE reserve_pub=$1;",
    1418             :                             1),
    1419             :     /* Used in #postgres_get_expired_reserves() */
    1420             :     GNUNET_PQ_make_prepare ("get_expired_reserves",
    1421             :                             "SELECT"
    1422             :                             " expiration_date"
    1423             :                             ",account_details"
    1424             :                             ",reserve_pub"
    1425             :                             ",current_balance_val"
    1426             :                             ",current_balance_frac"
    1427             :                             ",current_balance_curr"
    1428             :                             " FROM reserves"
    1429             :                             " WHERE expiration_date<=$1"
    1430             :                             "   AND (current_balance_val != 0 "
    1431             :                             "        OR current_balance_frac != 0)"
    1432             :                             " ORDER BY expiration_date ASC"
    1433             :                             " LIMIT 1;",
    1434             :                             1),
    1435             :     /* Used in #postgres_get_coin_transactions() to obtain payback transactions
    1436             :        for a coin */
    1437             :     GNUNET_PQ_make_prepare ("payback_by_coin",
    1438             :                             "SELECT"
    1439             :                             " ro.reserve_pub"
    1440             :                             ",coin_sig"
    1441             :                             ",coin_blind"
    1442             :                             ",amount_val"
    1443             :                             ",amount_frac"
    1444             :                             ",amount_curr"
    1445             :                             ",timestamp"
    1446             :                             ",denoms.denom_pub"
    1447             :                             ",coins.denom_sig"
    1448             :                             " FROM payback"
    1449             :                             "    JOIN known_coins coins"
    1450             :                             "      USING (coin_pub)"
    1451             :                             "    JOIN denominations denoms"
    1452             :                             "      USING (denom_pub_hash)"
    1453             :                             "    JOIN reserves_out ro"
    1454             :                             "      USING (h_blind_ev)"
    1455             :                             " WHERE payback.coin_pub=$1;",
    1456             :                             1),
    1457             :     /* Used in #postgres_get_reserve_by_h_blind() */
    1458             :     GNUNET_PQ_make_prepare ("reserve_by_h_blind",
    1459             :                             "SELECT"
    1460             :                             " reserve_pub"
    1461             :                             " FROM reserves_out"
    1462             :                             " WHERE h_blind_ev=$1"
    1463             :                             " LIMIT 1;",
    1464             :                             1),
    1465             :     /* used in #postgres_commit */
    1466             :     GNUNET_PQ_make_prepare ("do_commit",
    1467             :                             "COMMIT",
    1468             :                             0),
    1469             :     GNUNET_PQ_make_prepare ("gc_denominations",
    1470             :                             "DELETE"
    1471             :                             " FROM denominations"
    1472             :                             " WHERE expire_legal < $1;",
    1473             :                             1),
    1474             :     GNUNET_PQ_make_prepare ("gc_reserves",
    1475             :                             "DELETE"
    1476             :                             " FROM reserves"
    1477             :                             " WHERE expiration_date < $1"
    1478             :                             "   AND current_balance_val = 0"
    1479             :                             "   AND current_balance_frac = 0;",
    1480             :                             1),
    1481             :     GNUNET_PQ_make_prepare ("gc_wire_fee",
    1482             :                             "DELETE"
    1483             :                             " FROM wire_fee"
    1484             :                             " WHERE end_date < $1;",
    1485             :                             1),
    1486             :     GNUNET_PQ_PREPARED_STATEMENT_END
    1487             :   };
    1488             : 
    1489          38 :   return GNUNET_PQ_prepare_statements (db_conn,
    1490             :                                        ps);
    1491             : }
    1492             : 
    1493             : 
    1494             : /**
    1495             :  * Close thread-local database connection when a thread is destroyed.
    1496             :  *
    1497             :  * @param cls closure we get from pthreads (the db handle)
    1498             :  */
    1499             : static void
    1500           2 : db_conn_destroy (void *cls)
    1501             : {
    1502           2 :   struct TALER_EXCHANGEDB_Session *session = cls;
    1503             :   PGconn *db_conn;
    1504             : 
    1505           2 :   if (NULL == session)
    1506           0 :     return;
    1507           2 :   db_conn = session->conn;
    1508           2 :   if (NULL != db_conn)
    1509           2 :     PQfinish (db_conn);
    1510           2 :   GNUNET_free (session);
    1511             : }
    1512             : 
    1513             : 
    1514             : /**
    1515             :  * Get the thread-local database-handle.
    1516             :  * Connect to the db if the connection does not exist yet.
    1517             :  *
    1518             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    1519             :  * @return the database connection, or NULL on error
    1520             :  */
    1521             : static struct TALER_EXCHANGEDB_Session *
    1522         248 : postgres_get_session (void *cls)
    1523             : {
    1524         248 :   struct PostgresClosure *pc = cls;
    1525             :   PGconn *db_conn;
    1526             :   struct TALER_EXCHANGEDB_Session *session;
    1527             : 
    1528         248 :   if (NULL != (session = pthread_getspecific (pc->db_conn_threadlocal)))
    1529             :   {
    1530         211 :     if (CONNECTION_BAD == PQstatus (session->conn))
    1531             :     {
    1532             :       /**
    1533             :        * Reset the thread-local database-handle.  Disconnects from the
    1534             :        * DB.  Needed after the database server restarts as we need to
    1535             :        * properly reconnect. */
    1536           0 :       GNUNET_assert (0 ==
    1537             :                      pthread_setspecific (pc->db_conn_threadlocal,
    1538             :                                           NULL));
    1539           0 :       PQfinish (session->conn);
    1540           0 :       GNUNET_free (session);
    1541             :     }
    1542             :     else
    1543             :     {
    1544         211 :       return session;
    1545             :     }
    1546             :   }
    1547          37 :   db_conn = GNUNET_PQ_connect (pc->connection_cfg_str);
    1548          37 :   if (NULL == db_conn)
    1549           0 :     return NULL;
    1550          37 :   if (GNUNET_OK !=
    1551          37 :       postgres_prepare (db_conn))
    1552             :   {
    1553           0 :     GNUNET_break (0);
    1554           0 :     PQfinish (db_conn);
    1555           0 :     return NULL;
    1556             :   }
    1557          37 :   session = GNUNET_new (struct TALER_EXCHANGEDB_Session);
    1558          37 :   session->conn = db_conn;
    1559          37 :   if (0 != pthread_setspecific (pc->db_conn_threadlocal,
    1560             :                                 session))
    1561             :   {
    1562           0 :     GNUNET_break (0);
    1563           0 :     PQfinish (db_conn);
    1564           0 :     GNUNET_free (session);
    1565           0 :     return NULL;
    1566             :   }
    1567          37 :   return session;
    1568             : }
    1569             : 
    1570             : 
    1571             : /**
    1572             :  * Start a transaction.
    1573             :  *
    1574             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    1575             :  * @param session the database connection
    1576             :  * @return #GNUNET_OK on success
    1577             :  */
    1578             : static int
    1579         221 : postgres_start (void *cls,
    1580             :                 struct TALER_EXCHANGEDB_Session *session)
    1581             : {
    1582             :   PGresult *result;
    1583             :   ExecStatusType ex;
    1584             : 
    1585         221 :   result = PQexec (session->conn,
    1586             :                    "START TRANSACTION ISOLATION LEVEL SERIALIZABLE");
    1587         221 :   if (PGRES_COMMAND_OK !=
    1588             :       (ex = PQresultStatus (result)))
    1589             :   {
    1590           0 :     TALER_LOG_ERROR ("Failed to start transaction (%s): %s\n",
    1591             :                      PQresStatus (ex),
    1592             :                      PQerrorMessage (session->conn));
    1593           0 :     GNUNET_break (0);
    1594           0 :     PQclear (result);
    1595           0 :     return GNUNET_SYSERR;
    1596             :   }
    1597         221 :   PQclear (result);
    1598         221 :   return GNUNET_OK;
    1599             : }
    1600             : 
    1601             : 
    1602             : /**
    1603             :  * Roll back the current transaction of a database connection.
    1604             :  *
    1605             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    1606             :  * @param session the database connection
    1607             :  * @return #GNUNET_OK on success
    1608             :  */
    1609             : static void
    1610         146 : postgres_rollback (void *cls,
    1611             :                    struct TALER_EXCHANGEDB_Session *session)
    1612             : {
    1613             :   PGresult *result;
    1614             : 
    1615         146 :   result = PQexec (session->conn,
    1616             :                    "ROLLBACK");
    1617         146 :   GNUNET_break (PGRES_COMMAND_OK ==
    1618             :                 PQresultStatus (result));
    1619         146 :   PQclear (result);
    1620         146 : }
    1621             : 
    1622             : 
    1623             : /**
    1624             :  * Commit the current transaction of a database connection.
    1625             :  *
    1626             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    1627             :  * @param session the database connection
    1628             :  * @return final transaction status
    1629             :  */
    1630             : static enum GNUNET_DB_QueryStatus
    1631         138 : postgres_commit (void *cls,
    1632             :                  struct TALER_EXCHANGEDB_Session *session)
    1633             : {
    1634         138 :   struct GNUNET_PQ_QueryParam params[] = {
    1635             :     GNUNET_PQ_query_param_end
    1636             :   };
    1637             : 
    1638         138 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    1639             :                                              "do_commit",
    1640             :                                              params);
    1641             : }
    1642             : 
    1643             : 
    1644             : /**
    1645             :  * Insert a denomination key's public information into the database for
    1646             :  * reference by auditors and other consistency checks.
    1647             :  *
    1648             :  * @param cls the @e cls of this struct with the plugin-specific state
    1649             :  * @param session connection to use
    1650             :  * @param denom_pub the public key used for signing coins of this denomination
    1651             :  * @param issue issuing information with value, fees and other info about the coin
    1652             :  * @return status of the query
    1653             :  */
    1654             : static enum GNUNET_DB_QueryStatus
    1655          24 : postgres_insert_denomination_info (void *cls,
    1656             :                                    struct TALER_EXCHANGEDB_Session *session,
    1657             :                                    const struct TALER_DenominationPublicKey *denom_pub,
    1658             :                                    const struct TALER_EXCHANGEDB_DenominationKeyInformationP *issue)
    1659             : {
    1660         312 :   struct GNUNET_PQ_QueryParam params[] = {
    1661          24 :     GNUNET_PQ_query_param_auto_from_type (&issue->properties.denom_hash),
    1662          24 :     GNUNET_PQ_query_param_rsa_public_key (denom_pub->rsa_public_key),
    1663          24 :     GNUNET_PQ_query_param_auto_from_type (&issue->properties.master),
    1664          24 :     GNUNET_PQ_query_param_auto_from_type (&issue->signature),
    1665          24 :     GNUNET_PQ_query_param_absolute_time_nbo (&issue->properties.start),
    1666          24 :     GNUNET_PQ_query_param_absolute_time_nbo (&issue->properties.expire_withdraw),
    1667          24 :     GNUNET_PQ_query_param_absolute_time_nbo (&issue->properties.expire_deposit),
    1668          24 :     GNUNET_PQ_query_param_absolute_time_nbo (&issue->properties.expire_legal),
    1669          24 :     TALER_PQ_query_param_amount_nbo (&issue->properties.value),
    1670          24 :     TALER_PQ_query_param_amount_nbo (&issue->properties.fee_withdraw),
    1671          24 :     TALER_PQ_query_param_amount_nbo (&issue->properties.fee_deposit),
    1672          24 :     TALER_PQ_query_param_amount_nbo (&issue->properties.fee_refresh),
    1673          24 :     TALER_PQ_query_param_amount_nbo (&issue->properties.fee_refund),
    1674             :     GNUNET_PQ_query_param_end
    1675             :   };
    1676             : 
    1677             :   /* check fees match coin currency */
    1678          24 :   GNUNET_assert (GNUNET_YES ==
    1679             :                  TALER_amount_cmp_currency_nbo (&issue->properties.value,
    1680             :                                                 &issue->properties.fee_withdraw));
    1681          24 :   GNUNET_assert (GNUNET_YES ==
    1682             :                  TALER_amount_cmp_currency_nbo (&issue->properties.value,
    1683             :                                                 &issue->properties.fee_deposit));
    1684          24 :   GNUNET_assert (GNUNET_YES ==
    1685             :                  TALER_amount_cmp_currency_nbo (&issue->properties.value,
    1686             :                                                 &issue->properties.fee_refresh));
    1687          24 :   GNUNET_assert (GNUNET_YES ==
    1688             :                  TALER_amount_cmp_currency_nbo (&issue->properties.value,
    1689             :                                                &issue->properties.fee_refund));
    1690             : 
    1691          24 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    1692             :                                              "denomination_insert",
    1693             :                                              params);
    1694             : }
    1695             : 
    1696             : 
    1697             : /**
    1698             :  * Fetch information about a denomination key.
    1699             :  *
    1700             :  * @param cls the @e cls of this struct with the plugin-specific state
    1701             :  * @param session connection to use
    1702             :  * @param denom_pub the public key used for signing coins of this denomination
    1703             :  * @param[out] issue set to issue information with value, fees and other info about the coin
    1704             :  * @return transaction status code
    1705             :  */
    1706             : static enum GNUNET_DB_QueryStatus
    1707          37 : postgres_get_denomination_info (void *cls,
    1708             :                                 struct TALER_EXCHANGEDB_Session *session,
    1709             :                                 const struct TALER_DenominationPublicKey *denom_pub,
    1710             :                                 struct TALER_EXCHANGEDB_DenominationKeyInformationP *issue)
    1711             : {
    1712             :   enum GNUNET_DB_QueryStatus qs;
    1713             :   struct GNUNET_HashCode dph;
    1714          37 :   struct GNUNET_PQ_QueryParam params[] = {
    1715             :     GNUNET_PQ_query_param_auto_from_type (&dph),
    1716             :     GNUNET_PQ_query_param_end
    1717             :   };
    1718         407 :   struct GNUNET_PQ_ResultSpec rs[] = {
    1719          37 :     GNUNET_PQ_result_spec_auto_from_type ("master_pub",
    1720             :                                           &issue->properties.master),
    1721          37 :     GNUNET_PQ_result_spec_auto_from_type ("master_sig",
    1722             :                                           &issue->signature),
    1723          37 :     GNUNET_PQ_result_spec_absolute_time_nbo ("valid_from",
    1724             :                                              &issue->properties.start),
    1725          37 :     GNUNET_PQ_result_spec_absolute_time_nbo ("expire_withdraw",
    1726             :                                              &issue->properties.expire_withdraw),
    1727          37 :     GNUNET_PQ_result_spec_absolute_time_nbo ("expire_deposit",
    1728             :                                              &issue->properties.expire_deposit),
    1729          37 :     GNUNET_PQ_result_spec_absolute_time_nbo ("expire_legal",
    1730             :                                              &issue->properties.expire_legal),
    1731          37 :     TALER_PQ_result_spec_amount_nbo ("coin",
    1732             :                                      &issue->properties.value),
    1733          37 :     TALER_PQ_result_spec_amount_nbo ("fee_withdraw",
    1734             :                                      &issue->properties.fee_withdraw),
    1735          37 :     TALER_PQ_result_spec_amount_nbo ("fee_deposit",
    1736             :                                      &issue->properties.fee_deposit),
    1737          37 :     TALER_PQ_result_spec_amount_nbo ("fee_refresh",
    1738             :                                      &issue->properties.fee_refresh),
    1739          37 :     TALER_PQ_result_spec_amount_nbo ("fee_refund",
    1740             :                                      &issue->properties.fee_refund),
    1741             :     GNUNET_PQ_result_spec_end
    1742             :   };
    1743             : 
    1744          37 :   GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
    1745             :                                      &dph);
    1746          37 :   qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    1747             :                                                  "denomination_get",
    1748             :                                                  params,
    1749             :                                                  rs);
    1750          37 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    1751          14 :     return qs;
    1752          23 :   issue->properties.purpose.size = htonl (sizeof (struct TALER_DenominationKeyValidityPS));
    1753          23 :   issue->properties.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
    1754          23 :   GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
    1755             :                                      &issue->properties.denom_hash);
    1756          23 :   return qs;
    1757             : }
    1758             : 
    1759             : 
    1760             : /**
    1761             :  * Get the summary of a reserve.
    1762             :  *
    1763             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    1764             :  * @param session the database connection handle
    1765             :  * @param[in,out] reserve the reserve data.  The public key of the reserve should be
    1766             :  *          set in this structure; it is used to query the database.  The balance
    1767             :  *          and expiration are then filled accordingly.
    1768             :  * @return transaction status
    1769             :  */
    1770             : static enum GNUNET_DB_QueryStatus
    1771          31 : postgres_reserve_get (void *cls,
    1772             :                       struct TALER_EXCHANGEDB_Session *session,
    1773             :                       struct TALER_EXCHANGEDB_Reserve *reserve)
    1774             : {
    1775          31 :   struct GNUNET_PQ_QueryParam params[] = {
    1776          31 :     GNUNET_PQ_query_param_auto_from_type(&reserve->pub),
    1777             :     GNUNET_PQ_query_param_end
    1778             :   };
    1779          62 :   struct GNUNET_PQ_ResultSpec rs[] = {
    1780          31 :     TALER_PQ_result_spec_amount("current_balance", &reserve->balance),
    1781          31 :     GNUNET_PQ_result_spec_absolute_time("expiration_date", &reserve->expiry),
    1782             :     GNUNET_PQ_result_spec_end
    1783             :   };
    1784             : 
    1785          31 :   return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    1786             :                                                    "reserve_get",
    1787             :                                                    params,
    1788             :                                                    rs);
    1789             : }
    1790             : 
    1791             : 
    1792             : /**
    1793             :  * Updates a reserve with the data from the given reserve structure.
    1794             :  *
    1795             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    1796             :  * @param session the database connection
    1797             :  * @param reserve the reserve structure whose data will be used to update the
    1798             :  *          corresponding record in the database.
    1799             :  * @return transaction status
    1800             :  */
    1801             : static enum GNUNET_DB_QueryStatus
    1802          15 : reserves_update (void *cls,
    1803             :                  struct TALER_EXCHANGEDB_Session *session,
    1804             :                  const struct TALER_EXCHANGEDB_Reserve *reserve)
    1805             : {
    1806          45 :   struct GNUNET_PQ_QueryParam params[] = {
    1807          15 :     GNUNET_PQ_query_param_absolute_time (&reserve->expiry),
    1808          15 :     TALER_PQ_query_param_amount (&reserve->balance),
    1809          15 :     GNUNET_PQ_query_param_auto_from_type (&reserve->pub),
    1810             :     GNUNET_PQ_query_param_end
    1811             :   };
    1812             : 
    1813          15 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    1814             :                                              "reserve_update",
    1815             :                                              params);
    1816             : }
    1817             : 
    1818             : 
    1819             : /**
    1820             :  * Insert an incoming transaction into reserves.  New reserves are also created
    1821             :  * through this function.  Note that this API call starts (and stops) its
    1822             :  * own transaction scope (so the application must not do so).
    1823             :  *
    1824             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    1825             :  * @param session the database connection handle
    1826             :  * @param reserve_pub public key of the reserve
    1827             :  * @param balance the amount that has to be added to the reserve
    1828             :  * @param execution_time when was the amount added
    1829             :  * @param sender_account_details account information for the sender
    1830             :  * @param wire_reference unique reference identifying the wire transfer (binary blob)
    1831             :  * @param wire_reference_size number of bytes in @a wire_reference
    1832             :  * @return transaction status code
    1833             :  */
    1834             : static enum GNUNET_DB_QueryStatus
    1835          13 : postgres_reserves_in_insert (void *cls,
    1836             :                              struct TALER_EXCHANGEDB_Session *session,
    1837             :                              const struct TALER_ReservePublicKeyP *reserve_pub,
    1838             :                              const struct TALER_Amount *balance,
    1839             :                              struct GNUNET_TIME_Absolute execution_time,
    1840             :                              const json_t *sender_account_details,
    1841             :                              const void *wire_reference,
    1842             :                              size_t wire_reference_size)
    1843             : {
    1844          13 :   struct PostgresClosure *pg = cls;
    1845             :   enum GNUNET_DB_QueryStatus reserve_exists;
    1846             :   enum GNUNET_DB_QueryStatus qs;
    1847             :   struct TALER_EXCHANGEDB_Reserve reserve;
    1848             :   struct GNUNET_TIME_Absolute expiry;
    1849             : 
    1850          13 :   reserve.pub = *reserve_pub;
    1851          13 :   reserve_exists = postgres_reserve_get (cls,
    1852             :                                          session,
    1853             :                                          &reserve);
    1854          13 :   if (0 > reserve_exists)
    1855             :   {
    1856           0 :     GNUNET_break (0);
    1857           0 :     return reserve_exists;
    1858             :   }
    1859          13 :   if ( (0 == reserve.balance.value) &&
    1860           1 :        (0 == reserve.balance.fraction) )
    1861             :   {
    1862             :     /* TODO: reserve balance is empty, we might want to update
    1863             :        sender_account_details here.  (So that IF a customer uses the
    1864             :        same reserve public key from a different account, we USUALLY
    1865             :        switch to the new account (but only if the old reserve was
    1866             :        drained).)  This helps make sure that on reserve expiration the
    1867             :        funds go back to a valid account in cases where the customer
    1868             :        has closed the old bank account and some (buggy?) wallet keeps
    1869             :        using the same reserve key with the customer's new account.
    1870             : 
    1871             :        Note that for a non-drained reserve we should not switch,
    1872             :        as that opens an attack vector for an adversary who can see
    1873             :        the wire transfer subjects (i.e. when using Bitcoin).
    1874             :     */
    1875             :   }
    1876             : 
    1877          13 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1878             :               "Creating reserve %s with expiration in %s\n",
    1879             :               TALER_B2S (reserve_pub),
    1880             :               GNUNET_STRINGS_relative_time_to_string (pg->idle_reserve_expiration_time,
    1881             :                                                       GNUNET_NO));
    1882          13 :   expiry = GNUNET_TIME_absolute_add (execution_time,
    1883             :                                      pg->idle_reserve_expiration_time);
    1884          13 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == reserve_exists)
    1885             :   {
    1886             :     /* New reserve, create balance for the first time; we do this
    1887             :        before adding the actual transaction to "reserves_in", as
    1888             :        for a new reserve it can't be a duplicate 'add' operation,
    1889             :        and as the 'add' operation may need the reserve entry
    1890             :        as a foreign key. */
    1891          12 :     struct GNUNET_PQ_QueryParam params[] = {
    1892             :       GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    1893             :       TALER_PQ_query_param_json (sender_account_details),
    1894             :       TALER_PQ_query_param_amount (balance),
    1895             :       GNUNET_PQ_query_param_absolute_time (&expiry),
    1896             :       GNUNET_PQ_query_param_end
    1897             :     };
    1898             : 
    1899          12 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1900             :                 "Reserve does not exist; creating a new one\n");
    1901          12 :     qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
    1902             :                                              "reserve_create",
    1903             :                                              params);
    1904          12 :     if (0 > qs)
    1905           0 :       return qs;
    1906          12 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1907             :     {
    1908             :       /* Maybe DB did not detect serializiability error already,
    1909             :          but clearly there must be one. Still odd. */
    1910           0 :       GNUNET_break (0);
    1911           0 :       return GNUNET_DB_STATUS_SOFT_ERROR;
    1912             :     }
    1913             :   }
    1914             :   /* Create new incoming transaction, SQL "primary key" logic
    1915             :      is used to guard against duplicates.  If a duplicate is
    1916             :      detected, we just "succeed" with no changes. */
    1917             :   {
    1918          13 :     struct GNUNET_PQ_QueryParam params[] = {
    1919             :       GNUNET_PQ_query_param_auto_from_type (&reserve.pub),
    1920             :       GNUNET_PQ_query_param_fixed_size (wire_reference,
    1921             :                                         wire_reference_size),
    1922             :       TALER_PQ_query_param_amount (balance),
    1923             :       TALER_PQ_query_param_json (sender_account_details),
    1924             :       GNUNET_PQ_query_param_absolute_time (&execution_time),
    1925             :       GNUNET_PQ_query_param_end
    1926             :     };
    1927             : 
    1928          13 :     qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
    1929             :                                              "reserves_in_add_transaction",
    1930             :                                              params);
    1931          13 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    1932           0 :       return qs;
    1933             :   }
    1934             : 
    1935          13 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == reserve_exists)
    1936             :   {
    1937             :     /* If the reserve already existed, we need to still update the
    1938             :        balance; we do this after checking for duplication, as
    1939             :        otherwise we might have to actually pay the cost to roll this
    1940             :        back for duplicate transactions; like this, we should virtually
    1941             :        never actually have to rollback anything. */
    1942             :     struct TALER_EXCHANGEDB_Reserve updated_reserve;
    1943             :     
    1944           1 :     updated_reserve.pub = reserve.pub;
    1945           1 :     if (GNUNET_OK !=
    1946           1 :         TALER_amount_add (&updated_reserve.balance,
    1947             :                           &reserve.balance,
    1948             :                           balance))
    1949             :     {
    1950             :       /* currency overflow or incompatible currency */
    1951           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1952             :                   "Attempt to deposit incompatible amount into reserve\n");
    1953           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    1954             :     }
    1955           1 :     updated_reserve.expiry = GNUNET_TIME_absolute_max (expiry,
    1956             :                                                        reserve.expiry);
    1957           1 :     return reserves_update (cls,
    1958             :                             session,
    1959             :                             &updated_reserve);
    1960             :   }
    1961          12 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1962             : }
    1963             : 
    1964             : 
    1965             : /**
    1966             :  * Obtain the most recent @a wire_reference that was inserted via @e reserves_in_insert.
    1967             :  *
    1968             :  * @param cls the @e cls of this struct with the plugin-specific state
    1969             :  * @param session the database session handle
    1970             :  * @param[out] wire_reference set to unique reference identifying the wire transfer (binary blob)
    1971             :  * @param[out] wire_reference_size set to number of bytes in @a wire_reference
    1972             :  * @return transaction status code
    1973             :  */
    1974             : static enum GNUNET_DB_QueryStatus
    1975           6 : postgres_get_latest_reserve_in_reference (void *cls,
    1976             :                                           struct TALER_EXCHANGEDB_Session *session,
    1977             :                                           void **wire_reference,
    1978             :                                           size_t *wire_reference_size)
    1979             : {
    1980           6 :   struct GNUNET_PQ_QueryParam params[] = {
    1981             :     GNUNET_PQ_query_param_end
    1982             :   };
    1983           6 :   struct GNUNET_PQ_ResultSpec rs[] = {
    1984             :     GNUNET_PQ_result_spec_variable_size ("wire_reference",
    1985             :                                          wire_reference,
    1986             :                                          wire_reference_size),
    1987             :     GNUNET_PQ_result_spec_end
    1988             :   };
    1989             : 
    1990           6 :   return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    1991             :                                                    "reserves_in_get_latest_wire_reference",
    1992             :                                                    params,
    1993             :                                                    rs);
    1994             : }
    1995             : 
    1996             : 
    1997             : /**
    1998             :  * Locate the response for a /reserve/withdraw request under the
    1999             :  * key of the hash of the blinded message.
    2000             :  *
    2001             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    2002             :  * @param session database connection to use
    2003             :  * @param h_blind hash of the blinded coin to be signed (will match
    2004             :  *                `h_coin_envelope` in the @a collectable to be returned)
    2005             :  * @param collectable corresponding collectable coin (blind signature)
    2006             :  *                    if a coin is found
    2007             :  * @return statement execution status
    2008             :  */
    2009             : static enum GNUNET_DB_QueryStatus
    2010           8 : postgres_get_withdraw_info (void *cls,
    2011             :                             struct TALER_EXCHANGEDB_Session *session,
    2012             :                             const struct GNUNET_HashCode *h_blind,
    2013             :                             struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
    2014             : {
    2015           8 :   struct GNUNET_PQ_QueryParam params[] = {
    2016             :     GNUNET_PQ_query_param_auto_from_type (h_blind),
    2017             :     GNUNET_PQ_query_param_end
    2018             :   };
    2019          48 :   struct GNUNET_PQ_ResultSpec rs[] = {
    2020           8 :     GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    2021             :                                           &collectable->denom_pub.rsa_public_key),
    2022           8 :     GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    2023             :                                          &collectable->sig.rsa_signature),
    2024           8 :     GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
    2025             :                                           &collectable->reserve_sig),
    2026           8 :     GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    2027             :                                           &collectable->reserve_pub),
    2028           8 :     TALER_PQ_result_spec_amount ("amount_with_fee",
    2029             :                                  &collectable->amount_with_fee),
    2030           8 :     TALER_PQ_result_spec_amount ("fee_withdraw",
    2031             :                                  &collectable->withdraw_fee),
    2032             :     GNUNET_PQ_result_spec_end
    2033             :   };
    2034             : 
    2035           8 :   collectable->h_coin_envelope = *h_blind;
    2036           8 :   return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    2037             :                                                    "get_withdraw_info",
    2038             :                                                    params,
    2039             :                                                    rs);
    2040             : }
    2041             : 
    2042             : 
    2043             : /**
    2044             :  * Store collectable bit coin under the corresponding
    2045             :  * hash of the blinded message.
    2046             :  *
    2047             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    2048             :  * @param session database connection to use
    2049             :  * @param collectable corresponding collectable coin (blind signature)
    2050             :  *                    if a coin is found
    2051             :  * @return query execution status
    2052             :  */
    2053             : static enum GNUNET_DB_QueryStatus
    2054           8 : postgres_insert_withdraw_info (void *cls,
    2055             :                                struct TALER_EXCHANGEDB_Session *session,
    2056             :                                const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
    2057             : {
    2058           8 :   struct PostgresClosure *pg = cls;
    2059             :   struct TALER_EXCHANGEDB_Reserve reserve;
    2060             :   struct GNUNET_HashCode denom_pub_hash;
    2061             :   struct GNUNET_TIME_Absolute now;
    2062             :   struct GNUNET_TIME_Absolute expiry;
    2063          40 :   struct GNUNET_PQ_QueryParam params[] = {
    2064           8 :     GNUNET_PQ_query_param_auto_from_type (&collectable->h_coin_envelope),
    2065             :     GNUNET_PQ_query_param_auto_from_type (&denom_pub_hash),
    2066           8 :     GNUNET_PQ_query_param_rsa_signature (collectable->sig.rsa_signature),
    2067           8 :     GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_pub),
    2068           8 :     GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_sig),
    2069             :     GNUNET_PQ_query_param_absolute_time (&now),
    2070           8 :     TALER_PQ_query_param_amount (&collectable->amount_with_fee),
    2071             :     GNUNET_PQ_query_param_end
    2072             :   };
    2073             :   enum GNUNET_DB_QueryStatus qs;
    2074             : 
    2075           8 :   now = GNUNET_TIME_absolute_get ();
    2076           8 :   GNUNET_CRYPTO_rsa_public_key_hash (collectable->denom_pub.rsa_public_key,
    2077             :                                      &denom_pub_hash);
    2078           8 :   qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
    2079             :                                            "insert_withdraw_info",
    2080             :                                            params);
    2081           8 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    2082             :   {
    2083           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2084           0 :     return qs;
    2085             :   }
    2086             : 
    2087             :   /* update reserve balance */
    2088           8 :   reserve.pub = collectable->reserve_pub;
    2089           8 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    2090             :       (qs = postgres_reserve_get (cls,
    2091             :                                   session,
    2092             :                                   &reserve)))
    2093             :   {
    2094             :     /* Should have been checked before we got here... */
    2095           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2096           0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    2097           0 :       qs = GNUNET_DB_STATUS_HARD_ERROR;
    2098           0 :     return qs;
    2099             :   }
    2100           8 :   if (GNUNET_SYSERR ==
    2101           8 :       TALER_amount_subtract (&reserve.balance,
    2102             :                              &reserve.balance,
    2103             :                              &collectable->amount_with_fee))
    2104             :   {
    2105             :     /* The reserve history was checked to make sure there is enough of a balance
    2106             :        left before we tried this; however, concurrent operations may have changed
    2107             :        the situation by now.  We should re-try the transaction.  */
    2108           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2109             :                 "Withdrawal from reserve `%s' refused due to balance missmatch. Retrying.\n",
    2110             :                 TALER_B2S (&collectable->reserve_pub));
    2111           0 :     return GNUNET_DB_STATUS_SOFT_ERROR;
    2112             :   }
    2113           8 :   expiry = GNUNET_TIME_absolute_add (now,
    2114             :                                      pg->idle_reserve_expiration_time);
    2115           8 :   reserve.expiry = GNUNET_TIME_absolute_max (expiry,
    2116             :                                              reserve.expiry);
    2117           8 :   qs = reserves_update (cls,
    2118             :                         session,
    2119             :                         &reserve);
    2120           8 :   GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
    2121           8 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    2122             :   {
    2123           0 :     GNUNET_break (0);
    2124           0 :     qs = GNUNET_DB_STATUS_HARD_ERROR;
    2125             :   }
    2126           8 :   return qs;
    2127             : }
    2128             : 
    2129             : 
    2130             : /**
    2131             :  * Closure for callbacks invoked via #postgres_get_reserve_history.
    2132             :  */
    2133             : struct ReserveHistoryContext
    2134             : {
    2135             : 
    2136             :   /**
    2137             :    * Which reserve are we building the history for?
    2138             :    */ 
    2139             :   const struct TALER_ReservePublicKeyP *reserve_pub;
    2140             :   
    2141             :   /**
    2142             :    * Where we build the history.
    2143             :    */
    2144             :   struct TALER_EXCHANGEDB_ReserveHistory *rh;
    2145             :  
    2146             :   /**
    2147             :    * Tail of @e rh list.
    2148             :    */
    2149             :   struct TALER_EXCHANGEDB_ReserveHistory *rh_tail;
    2150             : 
    2151             :   /**
    2152             :    * Set to #GNUNET_SYSERR on serious internal errors during
    2153             :    * the callbacks.
    2154             :    */ 
    2155             :   int status;
    2156             : };
    2157             : 
    2158             : 
    2159             : /**
    2160             :  * Append and return a fresh element to the reserve
    2161             :  * history kept in @a rhc.
    2162             :  *
    2163             :  * @param rhc where the history is kept
    2164             :  * @return the fresh element that was added
    2165             :  */ 
    2166             : static struct TALER_EXCHANGEDB_ReserveHistory *
    2167          19 : append_rh (struct ReserveHistoryContext *rhc)
    2168             : {
    2169             :   struct TALER_EXCHANGEDB_ReserveHistory *tail;
    2170             : 
    2171          19 :   tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory);
    2172          19 :   if (NULL != rhc->rh_tail)
    2173             :   {
    2174           9 :     rhc->rh_tail->next = tail;
    2175           9 :     rhc->rh_tail = tail;
    2176             :   }
    2177             :   else
    2178             :   {
    2179          10 :     rhc->rh_tail = tail;
    2180          10 :     rhc->rh = tail;
    2181             :   }
    2182          19 :   return tail;
    2183             : }
    2184             : 
    2185             : 
    2186             : /**
    2187             :  * Add bank transfers to result set for #postgres_get_reserve_history.
    2188             :  *
    2189             :  * @param cls a `struct ReserveHistoryContext *`
    2190             :  * @param result SQL result
    2191             :  * @param num_results number of rows in @a result
    2192             :  */
    2193             : static void
    2194          10 : add_bank_to_exchange (void *cls,
    2195             :                       PGresult *result,
    2196             :                       unsigned int num_results)
    2197             : {
    2198          10 :   struct ReserveHistoryContext *rhc = cls;
    2199             :   
    2200          31 :   while (0 < num_results)
    2201             :   {
    2202             :     struct TALER_EXCHANGEDB_BankTransfer *bt;
    2203             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    2204             : 
    2205          11 :     bt = GNUNET_new (struct TALER_EXCHANGEDB_BankTransfer);
    2206             :     {
    2207          44 :       struct GNUNET_PQ_ResultSpec rs[] = {
    2208          11 :         GNUNET_PQ_result_spec_variable_size ("wire_reference",
    2209             :                                              &bt->wire_reference,
    2210             :                                              &bt->wire_reference_size),
    2211          11 :         TALER_PQ_result_spec_amount ("credit",
    2212             :                                      &bt->amount),
    2213          11 :         GNUNET_PQ_result_spec_absolute_time ("execution_date",
    2214             :                                              &bt->execution_date),
    2215          11 :         TALER_PQ_result_spec_json ("sender_account_details",
    2216             :                                    &bt->sender_account_details),
    2217             :         GNUNET_PQ_result_spec_end
    2218             :       };
    2219             :       
    2220          22 :       if (GNUNET_OK !=
    2221          11 :           GNUNET_PQ_extract_result (result,
    2222             :                                     rs,
    2223             :                                     --num_results))
    2224             :       {
    2225           0 :         GNUNET_break (0);
    2226           0 :         GNUNET_free (bt);
    2227           0 :         rhc->status = GNUNET_SYSERR;
    2228           0 :         return;
    2229             :       }
    2230             :     }
    2231          11 :     bt->reserve_pub = *rhc->reserve_pub;
    2232          11 :     tail = append_rh (rhc);
    2233          11 :     tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE;
    2234          11 :     tail->details.bank = bt;
    2235             :   } /* end of 'while (0 < rows)' */
    2236             : }
    2237             : 
    2238             : 
    2239             : /**
    2240             :  * Add coin withdrawals to result set for #postgres_get_reserve_history.
    2241             :  *
    2242             :  * @param cls a `struct ReserveHistoryContext *`
    2243             :  * @param result SQL result
    2244             :  * @param num_results number of rows in @a result
    2245             :  */
    2246             : static void
    2247          10 : add_withdraw_coin (void *cls,
    2248             :                    PGresult *result,
    2249             :                    unsigned int num_results)
    2250             : {
    2251          10 :   struct ReserveHistoryContext *rhc = cls;
    2252             :   
    2253          25 :   while (0 < num_results)
    2254             :   {
    2255             :     struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc;
    2256             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    2257             : 
    2258           5 :     cbc = GNUNET_new (struct TALER_EXCHANGEDB_CollectableBlindcoin);
    2259             :     {
    2260          30 :       struct GNUNET_PQ_ResultSpec rs[] = {
    2261           5 :         GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
    2262             :                                               &cbc->h_coin_envelope),
    2263           5 :         GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    2264             :                                               &cbc->denom_pub.rsa_public_key),
    2265           5 :         GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    2266             :                                              &cbc->sig.rsa_signature),
    2267           5 :         GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
    2268             :                                               &cbc->reserve_sig),
    2269           5 :         TALER_PQ_result_spec_amount ("amount_with_fee",
    2270             :                                      &cbc->amount_with_fee),
    2271           5 :         TALER_PQ_result_spec_amount ("fee_withdraw",
    2272             :                                      &cbc->withdraw_fee),
    2273             :         GNUNET_PQ_result_spec_end
    2274             :       };
    2275             :       
    2276          10 :       if (GNUNET_OK !=
    2277           5 :           GNUNET_PQ_extract_result (result,
    2278             :                                     rs,
    2279             :                                     --num_results))
    2280             :       {
    2281           0 :         GNUNET_break (0);
    2282           0 :         GNUNET_free (cbc);
    2283           0 :         rhc->status = GNUNET_SYSERR;
    2284           0 :         return;
    2285             :       }
    2286             :     }
    2287           5 :     cbc->reserve_pub = *rhc->reserve_pub;
    2288           5 :     tail = append_rh (rhc);
    2289           5 :     tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN;
    2290           5 :     tail->details.withdraw = cbc;
    2291             :   }
    2292             : }
    2293             : 
    2294             : 
    2295             : /**
    2296             :  * Add paybacks to result set for #postgres_get_reserve_history.
    2297             :  *
    2298             :  * @param cls a `struct ReserveHistoryContext *`
    2299             :  * @param result SQL result
    2300             :  * @param num_results number of rows in @a result
    2301             :  */
    2302             : static void
    2303          10 : add_payback (void *cls,
    2304             :              PGresult *result,
    2305             :              unsigned int num_results)
    2306             : {
    2307          10 :   struct ReserveHistoryContext *rhc = cls;
    2308             :   
    2309          22 :   while (0 < num_results)
    2310             :   {
    2311             :     struct TALER_EXCHANGEDB_Payback *payback;
    2312             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    2313             : 
    2314           2 :     payback = GNUNET_new (struct TALER_EXCHANGEDB_Payback);
    2315             :     {
    2316          14 :       struct GNUNET_PQ_ResultSpec rs[] = {
    2317           2 :         TALER_PQ_result_spec_amount ("amount",
    2318             :                                      &payback->value),
    2319           2 :         GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    2320             :                                               &payback->coin.coin_pub),
    2321           2 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
    2322             :                                               &payback->coin_blind),
    2323           2 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    2324             :                                               &payback->coin_sig),
    2325           2 :         GNUNET_PQ_result_spec_absolute_time ("timestamp",
    2326             :                                              &payback->timestamp),
    2327           2 :         GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    2328             :                                               &payback->coin.denom_pub.rsa_public_key),
    2329           2 :         GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    2330             :                                              &payback->coin.denom_sig.rsa_signature),
    2331             :         GNUNET_PQ_result_spec_end
    2332             :       };
    2333             :       
    2334           4 :       if (GNUNET_OK !=
    2335           2 :           GNUNET_PQ_extract_result (result,
    2336             :                                     rs,
    2337             :                                     --num_results))
    2338             :       {
    2339           0 :         GNUNET_break (0);
    2340           0 :         GNUNET_free (payback);
    2341           0 :         rhc->status = GNUNET_SYSERR;
    2342           0 :         return;
    2343             :       }
    2344             :     }
    2345           2 :     payback->reserve_pub = *rhc->reserve_pub;
    2346           2 :     tail = append_rh (rhc);
    2347           2 :     tail->type = TALER_EXCHANGEDB_RO_PAYBACK_COIN;
    2348           2 :     tail->details.payback = payback;
    2349             :   } /* end of 'while (0 < rows)' */
    2350             : }
    2351             : 
    2352             : 
    2353             : /**
    2354             :  * Add exchange-to-bank transfers to result set for
    2355             :  * #postgres_get_reserve_history.
    2356             :  *
    2357             :  * @param cls a `struct ReserveHistoryContext *`
    2358             :  * @param result SQL result
    2359             :  * @param num_results number of rows in @a result
    2360             :  */
    2361             : static void
    2362          10 : add_exchange_to_bank (void *cls,
    2363             :                       PGresult *result,
    2364             :                       unsigned int num_results)
    2365             : {
    2366          10 :   struct ReserveHistoryContext *rhc = cls;
    2367             :   
    2368          21 :   while (0 < num_results)
    2369             :   {
    2370             :     struct TALER_EXCHANGEDB_ClosingTransfer *closing;
    2371             :     struct TALER_EXCHANGEDB_ReserveHistory *tail;
    2372             :       
    2373           1 :     closing = GNUNET_new (struct TALER_EXCHANGEDB_ClosingTransfer);
    2374             :     {
    2375           5 :       struct GNUNET_PQ_ResultSpec rs[] = {
    2376           1 :         TALER_PQ_result_spec_amount ("amount",
    2377             :                                      &closing->amount),
    2378           1 :         TALER_PQ_result_spec_amount ("closing_fee",
    2379             :                                      &closing->closing_fee),
    2380           1 :         GNUNET_PQ_result_spec_absolute_time ("execution_date",
    2381             :                                              &closing->execution_date),
    2382           1 :         TALER_PQ_result_spec_json ("receiver_account",
    2383             :                                    &closing->receiver_account_details),
    2384           1 :         GNUNET_PQ_result_spec_auto_from_type ("wtid",
    2385             :                                               &closing->wtid),
    2386             :         GNUNET_PQ_result_spec_end
    2387             :       };
    2388             :       
    2389           2 :       if (GNUNET_OK !=
    2390           1 :           GNUNET_PQ_extract_result (result,
    2391             :                                     rs,
    2392             :                                     --num_results))
    2393             :       {
    2394           0 :         GNUNET_break (0);
    2395           0 :         GNUNET_free (closing);
    2396           0 :         rhc->status = GNUNET_SYSERR;
    2397           0 :         return;
    2398             :       }
    2399             :     }
    2400           1 :     closing->reserve_pub = *rhc->reserve_pub;
    2401           1 :     tail = append_rh (rhc);
    2402           1 :     tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK;
    2403           1 :     tail->details.closing = closing;
    2404             :   } /* end of 'while (0 < rows)' */
    2405             : }
    2406             : 
    2407             : 
    2408             : /**
    2409             :  * Get all of the transaction history associated with the specified
    2410             :  * reserve.
    2411             :  *
    2412             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    2413             :  * @param session connection to use
    2414             :  * @param reserve_pub public key of the reserve
    2415             :  * @param[out] rhp set to known transaction history (NULL if reserve is unknown)
    2416             :  * @return transaction status
    2417             :  */
    2418             : static enum GNUNET_DB_QueryStatus
    2419          10 : postgres_get_reserve_history (void *cls,
    2420             :                               struct TALER_EXCHANGEDB_Session *session,
    2421             :                               const struct TALER_ReservePublicKeyP *reserve_pub,
    2422             :                               struct TALER_EXCHANGEDB_ReserveHistory **rhp)
    2423             : {
    2424             :   struct ReserveHistoryContext rhc;
    2425             :   struct {
    2426             :     /**
    2427             :      * Name of the prepared statement to run.
    2428             :      */
    2429             :     const char *statement;
    2430             :     /**
    2431             :      * Function to use to process the results.
    2432             :      */
    2433             :     GNUNET_PQ_PostgresResultHandler cb;
    2434          10 :   } work[] = {
    2435             :     /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */
    2436             :     { "reserves_in_get_transactions",
    2437             :       add_bank_to_exchange },
    2438             :     /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */
    2439             :     { "get_reserves_out",
    2440             :       &add_withdraw_coin },
    2441             :     /** #TALER_EXCHANGEDB_RO_PAYBACK_COIN */
    2442             :     { "payback_by_reserve",
    2443             :       &add_payback },
    2444             :     /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */
    2445             :     { "close_by_reserve",
    2446             :       &add_exchange_to_bank },
    2447             :     /* List terminator */
    2448             :     { NULL,
    2449             :       NULL }
    2450             :   };
    2451             :   enum GNUNET_DB_QueryStatus qs;
    2452          10 :   struct GNUNET_PQ_QueryParam params[] = {
    2453             :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    2454             :     GNUNET_PQ_query_param_end
    2455             :   };
    2456             : 
    2457          10 :   rhc.reserve_pub = reserve_pub;
    2458          10 :   rhc.rh = NULL;
    2459          10 :   rhc.rh_tail = NULL;
    2460          10 :   rhc.status = GNUNET_OK;
    2461          50 :   for (unsigned int i=0;NULL != work[i].cb;i++)
    2462             :   {
    2463          40 :     qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    2464             :                                                work[i].statement,
    2465             :                                                params,
    2466             :                                                work[i].cb,
    2467             :                                                &rhc);
    2468          80 :     if ( (0 > qs) ||
    2469          40 :          (GNUNET_OK != rhc.status) )
    2470             :       break;
    2471             :   }
    2472          20 :   if ( (qs < 0) ||
    2473          10 :        (rhc.status != GNUNET_OK) )
    2474             :   {
    2475           0 :     common_free_reserve_history (cls,
    2476             :                                  rhc.rh);
    2477           0 :     rhc.rh = NULL;
    2478           0 :     if (qs >= 0)
    2479             :     {
    2480             :       /* status == SYSERR is a very hard error... */
    2481           0 :       qs = GNUNET_DB_STATUS_HARD_ERROR;
    2482             :     }
    2483             :   }
    2484          10 :   *rhp = rhc.rh;  
    2485          10 :   return qs;
    2486             : }
    2487             : 
    2488             : 
    2489             : /**
    2490             :  * Check if we have the specified deposit already in the database.
    2491             :  *
    2492             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    2493             :  * @param session database connection
    2494             :  * @param deposit deposit to search for
    2495             :  * @return 1 if we know this operation,
    2496             :  *         0 if this exact deposit is unknown to us,
    2497             :  *         otherwise transaction error status
    2498             :  */
    2499             : static enum GNUNET_DB_QueryStatus
    2500          13 : postgres_have_deposit (void *cls,
    2501             :                        struct TALER_EXCHANGEDB_Session *session,
    2502             :                        const struct TALER_EXCHANGEDB_Deposit *deposit)
    2503             : {
    2504          39 :   struct GNUNET_PQ_QueryParam params[] = {
    2505          13 :     GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
    2506          13 :     GNUNET_PQ_query_param_auto_from_type (&deposit->h_contract_terms),
    2507          13 :     GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
    2508             :     GNUNET_PQ_query_param_end
    2509             :   };
    2510             :   struct TALER_EXCHANGEDB_Deposit deposit2;
    2511          13 :   struct GNUNET_PQ_ResultSpec rs[] = {
    2512             :     TALER_PQ_result_spec_amount ("amount_with_fee",
    2513             :                                  &deposit2.amount_with_fee),
    2514             :     GNUNET_PQ_result_spec_absolute_time ("timestamp",
    2515             :                                          &deposit2.timestamp),
    2516             :     GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
    2517             :                                          &deposit2.refund_deadline),
    2518             :     GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
    2519             :                                          &deposit2.wire_deadline),
    2520             :     GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    2521             :                                           &deposit2.h_contract_terms),
    2522             :     GNUNET_PQ_result_spec_auto_from_type ("h_wire",
    2523             :                                           &deposit2.h_wire),
    2524             :     GNUNET_PQ_result_spec_end
    2525             :   };
    2526             :   enum GNUNET_DB_QueryStatus qs;
    2527             : 
    2528          13 :   qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    2529             :                                                  "get_deposit",
    2530             :                                                  params,
    2531             :                                                  rs);
    2532          13 :   if (0 >= qs)
    2533          12 :     return qs;
    2534             :   /* Now we check that the other information in @a deposit
    2535             :      also matches, and if not report inconsistencies. */
    2536           1 :   if ( (0 != TALER_amount_cmp (&deposit->amount_with_fee,
    2537           1 :                                &deposit2.amount_with_fee)) ||
    2538           1 :        (deposit->timestamp.abs_value_us !=
    2539           2 :         deposit2.timestamp.abs_value_us) ||
    2540           1 :        (deposit->refund_deadline.abs_value_us !=
    2541           2 :         deposit2.refund_deadline.abs_value_us) ||
    2542           1 :        (0 != memcmp (&deposit->h_contract_terms,
    2543             :                      &deposit2.h_contract_terms,
    2544           1 :                      sizeof (struct GNUNET_HashCode))) ||
    2545           1 :        (0 != memcmp (&deposit->h_wire,
    2546             :                      &deposit2.h_wire,
    2547             :                      sizeof (struct GNUNET_HashCode))) )
    2548             :   {
    2549             :     /* Inconsistencies detected! Does not match!  (We might want to
    2550             :        expand the API with a 'get_deposit' function to return the
    2551             :        original transaction details to be used for an error message
    2552             :        in the future!) #3838 */
    2553           0 :     return 0; /* Counts as if the transaction was not there */
    2554             :   }
    2555           1 :   return 1;
    2556             : }
    2557             : 
    2558             : 
    2559             : /**
    2560             :  * Mark a deposit as tiny, thereby declaring that it cannot be
    2561             :  * executed by itself and should no longer be returned by
    2562             :  * @e iterate_ready_deposits()
    2563             :  *
    2564             :  * @param cls the @e cls of this struct with the plugin-specific state
    2565             :  * @param session connection to the database
    2566             :  * @param rowid identifies the deposit row to modify
    2567             :  * @return query result status
    2568             :  */
    2569             : static enum GNUNET_DB_QueryStatus
    2570          10 : postgres_mark_deposit_tiny (void *cls,
    2571             :                             struct TALER_EXCHANGEDB_Session *session,
    2572             :                             uint64_t rowid)
    2573             : {
    2574          10 :   struct GNUNET_PQ_QueryParam params[] = {
    2575             :     GNUNET_PQ_query_param_uint64 (&rowid),
    2576             :     GNUNET_PQ_query_param_end
    2577             :   };
    2578             : 
    2579          10 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    2580             :                                              "mark_deposit_tiny",
    2581             :                                              params);
    2582             : }
    2583             : 
    2584             : 
    2585             : /**
    2586             :  * Test if a deposit was marked as done, thereby declaring that it cannot be
    2587             :  * refunded anymore.
    2588             :  *
    2589             :  * @param cls the @e cls of this struct with the plugin-specific state
    2590             :  * @param session connection to the database
    2591             :  * @param deposit the deposit to check
    2592             :  * @return #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if is is marked done,
    2593             :  *         #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if not,
    2594             :  *         otherwise transaction error status (incl. deposit unknown)
    2595             :  */
    2596             : static enum GNUNET_DB_QueryStatus
    2597           4 : postgres_test_deposit_done (void *cls,
    2598             :                             struct TALER_EXCHANGEDB_Session *session,
    2599             :                             const struct TALER_EXCHANGEDB_Deposit *deposit)
    2600             : {
    2601          16 :   struct GNUNET_PQ_QueryParam params[] = {
    2602           4 :     GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
    2603           4 :     GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
    2604           4 :     GNUNET_PQ_query_param_auto_from_type (&deposit->h_contract_terms),
    2605           4 :     GNUNET_PQ_query_param_auto_from_type (&deposit->h_wire),
    2606             :     GNUNET_PQ_query_param_end
    2607             :   };
    2608           4 :   uint8_t done = 0;
    2609           4 :   struct GNUNET_PQ_ResultSpec rs[] = {
    2610             :     GNUNET_PQ_result_spec_auto_from_type ("done",
    2611             :                                           &done),
    2612             :     GNUNET_PQ_result_spec_end
    2613             :   };
    2614             :   enum GNUNET_DB_QueryStatus qs;
    2615             : 
    2616           4 :   qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    2617             :                                                  "test_deposit_done",
    2618             :                                                  params,
    2619             :                                                  rs);
    2620           4 :   if (qs < 0)
    2621           0 :     return qs;
    2622           4 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    2623           0 :     return GNUNET_DB_STATUS_HARD_ERROR; /* deposit MUST exist */
    2624             :   return (done
    2625             :           ? GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
    2626           4 :           : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS);
    2627             : }
    2628             : 
    2629             : 
    2630             : /**
    2631             :  * Mark a deposit as done, thereby declaring that it cannot be
    2632             :  * executed at all anymore, and should no longer be returned by
    2633             :  * @e iterate_ready_deposits() or @e iterate_matching_deposits().
    2634             :  *
    2635             :  * @param cls the @e cls of this struct with the plugin-specific state
    2636             :  * @param session connection to the database
    2637             :  * @param rowid identifies the deposit row to modify
    2638             :  * @return query result status
    2639             :  */
    2640             : static enum GNUNET_DB_QueryStatus
    2641          39 : postgres_mark_deposit_done (void *cls,
    2642             :                             struct TALER_EXCHANGEDB_Session *session,
    2643             :                             uint64_t rowid)
    2644             : {
    2645          39 :   struct GNUNET_PQ_QueryParam params[] = {
    2646             :     GNUNET_PQ_query_param_uint64 (&rowid),
    2647             :     GNUNET_PQ_query_param_end
    2648             :   };
    2649             : 
    2650          39 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    2651             :                                              "mark_deposit_done",
    2652             :                                              params);
    2653             : }
    2654             : 
    2655             : 
    2656             : /**
    2657             :  * Obtain information about deposits that are ready to be executed.
    2658             :  * Such deposits must not be marked as "tiny" or "done", and the
    2659             :  * execution time must be in the past.
    2660             :  *
    2661             :  * @param cls the @e cls of this struct with the plugin-specific state
    2662             :  * @param session connection to the database
    2663             :  * @param deposit_cb function to call for ONE such deposit
    2664             :  * @param deposit_cb_cls closure for @a deposit_cb
    2665             :  * @return transaction status code
    2666             :  */
    2667             : static enum GNUNET_DB_QueryStatus
    2668          64 : postgres_get_ready_deposit (void *cls,
    2669             :                             struct TALER_EXCHANGEDB_Session *session,
    2670             :                             TALER_EXCHANGEDB_DepositIterator deposit_cb,
    2671             :                             void *deposit_cb_cls)
    2672             : {
    2673          64 :   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
    2674          64 :   struct GNUNET_PQ_QueryParam params[] = {
    2675             :     GNUNET_PQ_query_param_absolute_time (&now),
    2676             :     GNUNET_PQ_query_param_end
    2677             :   };
    2678             :   struct TALER_Amount amount_with_fee;
    2679             :   struct TALER_Amount deposit_fee;
    2680             :   struct GNUNET_TIME_Absolute wire_deadline;
    2681             :   struct GNUNET_HashCode h_contract_terms;
    2682             :   struct TALER_MerchantPublicKeyP merchant_pub;
    2683             :   struct TALER_CoinSpendPublicKeyP coin_pub;
    2684             :   uint64_t serial_id;
    2685             :   json_t *wire;
    2686          64 :   struct GNUNET_PQ_ResultSpec rs[] = {
    2687             :     GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
    2688             :                                   &serial_id),
    2689             :     TALER_PQ_result_spec_amount ("amount_with_fee",
    2690             :                                  &amount_with_fee),
    2691             :     TALER_PQ_result_spec_amount ("fee_deposit",
    2692             :                                  &deposit_fee),
    2693             :     GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
    2694             :                                          &wire_deadline),
    2695             :     GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    2696             :                                           &h_contract_terms),
    2697             :     GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    2698             :                                           &merchant_pub),
    2699             :     GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    2700             :                                           &coin_pub),
    2701             :     TALER_PQ_result_spec_json ("wire",
    2702             :                                &wire),
    2703             :     GNUNET_PQ_result_spec_end
    2704             :   };
    2705             :   enum GNUNET_DB_QueryStatus qs;
    2706             : 
    2707          64 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2708             :               "Finding ready deposits by deadline %s (%llu)\n",
    2709             :               GNUNET_STRINGS_absolute_time_to_string (now),
    2710             :               (unsigned long long) now.abs_value_us);
    2711             : 
    2712          64 :   qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    2713             :                                                  "deposits_get_ready",
    2714             :                                                  params,
    2715             :                                                  rs);
    2716          64 :   if (qs <= 0)
    2717          41 :     return qs;
    2718          23 :   qs = deposit_cb (deposit_cb_cls,
    2719             :                    serial_id,
    2720             :                    &merchant_pub,
    2721             :                    &coin_pub,
    2722             :                    &amount_with_fee,
    2723             :                    &deposit_fee,
    2724             :                    &h_contract_terms,
    2725             :                    wire_deadline,
    2726             :                    wire);
    2727          23 :   GNUNET_PQ_cleanup_result (rs);
    2728          23 :   return qs;
    2729             : }
    2730             : 
    2731             : 
    2732             : /**
    2733             :  * Closure for #match_deposit_cb().
    2734             :  */
    2735             : struct MatchingDepositContext
    2736             : {
    2737             :   /**
    2738             :    * Function to call for each result
    2739             :    */
    2740             :   TALER_EXCHANGEDB_DepositIterator deposit_cb;
    2741             : 
    2742             :   /**
    2743             :    * Closure for @e deposit_cb.
    2744             :    */
    2745             :   void *deposit_cb_cls;
    2746             : 
    2747             :   /**
    2748             :    * Public key of the merchant against which we are matching.
    2749             :    */
    2750             :   const struct TALER_MerchantPublicKeyP *merchant_pub;
    2751             :   
    2752             :   /**
    2753             :    * Maximum number of results to return.
    2754             :    */
    2755             :   uint32_t limit;
    2756             : 
    2757             :   /**
    2758             :    * Loop counter, actual number of results returned.
    2759             :    */
    2760             :   unsigned int i;
    2761             : 
    2762             :   /**
    2763             :    * Set to #GNUNET_SYSERR on hard errors.
    2764             :    */
    2765             :   int status;
    2766             : };
    2767             : 
    2768             : 
    2769             : /**
    2770             :  * Helper function for #postgres_iterate_matching_deposits().
    2771             :  * To be called with the results of a SELECT statement
    2772             :  * that has returned @a num_results results.
    2773             :  *
    2774             :  * @param cls closure of type `struct MatchingDepositContext *`
    2775             :  * @param result the postgres result
    2776             :  * @param num_result the number of results in @a result
    2777             :  */
    2778             : static void
    2779          22 : match_deposit_cb (void *cls,
    2780             :                   PGresult *result,
    2781             :                   unsigned int num_results)
    2782             : {
    2783          22 :   struct MatchingDepositContext *mdc = cls;
    2784             :   
    2785          22 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2786             :               "Found %u/%u matching deposits\n",
    2787             :               num_results,
    2788             :               mdc->limit);
    2789          22 :   num_results = GNUNET_MIN (num_results,
    2790             :                             mdc->limit);
    2791          40 :   for (mdc->i=0;mdc->i<num_results;mdc->i++)
    2792             :   {
    2793             :     struct TALER_Amount amount_with_fee;
    2794             :     struct TALER_Amount deposit_fee;
    2795             :     struct GNUNET_TIME_Absolute wire_deadline;
    2796             :     struct GNUNET_HashCode h_contract_terms;
    2797             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    2798             :     uint64_t serial_id;
    2799             :     enum GNUNET_DB_QueryStatus qs;
    2800          18 :     struct GNUNET_PQ_ResultSpec rs[] = {
    2801             :       GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
    2802             :                                     &serial_id),
    2803             :       TALER_PQ_result_spec_amount ("amount_with_fee",
    2804             :                                    &amount_with_fee),
    2805             :       TALER_PQ_result_spec_amount ("fee_deposit",
    2806             :                                    &deposit_fee),
    2807             :       GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
    2808             :                                            &wire_deadline),
    2809             :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    2810             :                                             &h_contract_terms),
    2811             :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    2812             :                                             &coin_pub),
    2813             :       GNUNET_PQ_result_spec_end
    2814             :     };
    2815             :     
    2816          18 :     if (GNUNET_OK !=
    2817          18 :         GNUNET_PQ_extract_result (result,
    2818             :                                   rs,
    2819          18 :                                   mdc->i))
    2820             :     {
    2821           0 :       GNUNET_break (0);
    2822           0 :       mdc->status = GNUNET_SYSERR;
    2823           0 :       return;
    2824             :     }
    2825          18 :     qs = mdc->deposit_cb (mdc->deposit_cb_cls,
    2826             :                           serial_id,
    2827             :                           mdc->merchant_pub,
    2828             :                           &coin_pub,
    2829             :                           &amount_with_fee,
    2830             :                           &deposit_fee,
    2831             :                           &h_contract_terms,
    2832             :                           wire_deadline,
    2833             :                           NULL);
    2834          18 :     GNUNET_PQ_cleanup_result (rs);
    2835          18 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    2836           0 :       break;
    2837             :   }
    2838             : }
    2839             : 
    2840             : 
    2841             : /**
    2842             :  * Obtain information about other pending deposits for the same
    2843             :  * destination.  Those deposits must not already be "done".
    2844             :  *
    2845             :  * @param cls the @e cls of this struct with the plugin-specific state
    2846             :  * @param session connection to the database
    2847             :  * @param h_wire destination of the wire transfer
    2848             :  * @param merchant_pub public key of the merchant
    2849             :  * @param deposit_cb function to call for each deposit
    2850             :  * @param deposit_cb_cls closure for @a deposit_cb
    2851             :  * @param limit maximum number of matching deposits to return
    2852             :  * @return transaction status code, if positive:
    2853             :  *         number of rows processed, 0 if none exist
    2854             :  */
    2855             : static enum GNUNET_DB_QueryStatus
    2856          22 : postgres_iterate_matching_deposits (void *cls,
    2857             :                                     struct TALER_EXCHANGEDB_Session *session,
    2858             :                                     const struct GNUNET_HashCode *h_wire,
    2859             :                                     const struct TALER_MerchantPublicKeyP *merchant_pub,
    2860             :                                     TALER_EXCHANGEDB_DepositIterator deposit_cb,
    2861             :                                     void *deposit_cb_cls,
    2862             :                                     uint32_t limit)
    2863             : {
    2864          22 :   struct GNUNET_PQ_QueryParam params[] = {
    2865             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    2866             :     GNUNET_PQ_query_param_auto_from_type (h_wire),
    2867             :     GNUNET_PQ_query_param_end
    2868             :   };
    2869             :   struct MatchingDepositContext mdc;
    2870             :   enum GNUNET_DB_QueryStatus qs;
    2871             : 
    2872          22 :   mdc.deposit_cb = deposit_cb;
    2873          22 :   mdc.deposit_cb_cls = deposit_cb_cls;
    2874          22 :   mdc.merchant_pub = merchant_pub;
    2875          22 :   mdc.limit = limit;
    2876          22 :   mdc.status = GNUNET_OK;
    2877          22 :   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    2878             :                                              "deposits_iterate_matching",
    2879             :                                              params,
    2880             :                                              &match_deposit_cb,
    2881             :                                              &mdc);
    2882          22 :   if (GNUNET_OK != mdc.status)
    2883             :   {
    2884           0 :     GNUNET_break (0);
    2885           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    2886             :   }
    2887          22 :   if (qs >= 0)
    2888          22 :     return mdc.i;
    2889           0 :   return qs;
    2890             : }
    2891             : 
    2892             : 
    2893             : /**
    2894             :  * Retrieve the record for a known coin.
    2895             :  *
    2896             :  * @param cls the plugin closure
    2897             :  * @param session the database session handle
    2898             :  * @param coin_pub the public key of the coin to search for
    2899             :  * @param coin_info place holder for the returned coin information object
    2900             :  * @return transaction status code
    2901             :  */
    2902             : static enum GNUNET_DB_QueryStatus
    2903          59 : get_known_coin (void *cls,
    2904             :                 struct TALER_EXCHANGEDB_Session *session,
    2905             :                 const struct TALER_CoinSpendPublicKeyP *coin_pub,
    2906             :                 struct TALER_CoinPublicInfo *coin_info)
    2907             : {
    2908          59 :   struct GNUNET_PQ_QueryParam params[] = {
    2909             :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    2910             :     GNUNET_PQ_query_param_end
    2911             :   };
    2912         118 :   struct GNUNET_PQ_ResultSpec rs[] = {
    2913          59 :     GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    2914             :                                           &coin_info->denom_pub.rsa_public_key),
    2915          59 :     GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    2916             :                                          &coin_info->denom_sig.rsa_signature),
    2917             :     GNUNET_PQ_result_spec_end
    2918             :   };
    2919             :   
    2920          59 :   coin_info->coin_pub = *coin_pub;
    2921          59 :   return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    2922             :                                                    "get_known_coin",
    2923             :                                                    params,
    2924             :                                                    rs);
    2925             : }
    2926             : 
    2927             : 
    2928             : /**
    2929             :  * Insert a coin we know of into the DB.  The coin can then be
    2930             :  * referenced by tables for deposits, refresh and refund
    2931             :  * functionality.
    2932             :  *
    2933             :  * @param cls plugin closure
    2934             :  * @param session the shared database session
    2935             :  * @param coin_info the public coin info
    2936             :  * @return query result status
    2937             :  */
    2938             : static enum GNUNET_DB_QueryStatus
    2939          12 : insert_known_coin (void *cls,
    2940             :                    struct TALER_EXCHANGEDB_Session *session,
    2941             :                    const struct TALER_CoinPublicInfo *coin_info)
    2942             : {
    2943             :   struct GNUNET_HashCode denom_pub_hash;
    2944          24 :   struct GNUNET_PQ_QueryParam params[] = {
    2945          12 :     GNUNET_PQ_query_param_auto_from_type (&coin_info->coin_pub),
    2946             :     GNUNET_PQ_query_param_auto_from_type (&denom_pub_hash),
    2947          12 :     GNUNET_PQ_query_param_rsa_signature (coin_info->denom_sig.rsa_signature),
    2948             :     GNUNET_PQ_query_param_end
    2949             :   };
    2950             : 
    2951          12 :   GNUNET_CRYPTO_rsa_public_key_hash (coin_info->denom_pub.rsa_public_key,
    2952             :                                      &denom_pub_hash);
    2953          12 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    2954             :                                              "insert_known_coin",
    2955             :                                              params);
    2956             : }
    2957             : 
    2958             : 
    2959             : /**
    2960             :  * Make sure the given @a coin is known to the database.
    2961             :  *
    2962             :  * @param cls database connection plugin state
    2963             :  * @param session database session
    2964             :  * @param coin the coin that must be made known
    2965             :  * @return database transaction status, non-negative on success
    2966             :  */ 
    2967             : static enum GNUNET_DB_QueryStatus
    2968          39 : ensure_coin_known (struct PostgresClosure *cls,
    2969             :                    struct TALER_EXCHANGEDB_Session *session,
    2970             :                    const struct TALER_CoinPublicInfo *coin)
    2971             : {
    2972             :   enum GNUNET_DB_QueryStatus qs;
    2973             :   struct TALER_CoinPublicInfo known_coin;
    2974             : 
    2975             :   /* check if the coin is already known */
    2976          39 :   qs = get_known_coin (cls,
    2977             :                        session,
    2978             :                        &coin->coin_pub,
    2979             :                        &known_coin);
    2980          39 :   if (0 > qs)
    2981             :   {
    2982           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2983           0 :     return GNUNET_SYSERR;
    2984             :   }
    2985          39 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    2986          27 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* no change! */
    2987          12 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
    2988             :   /* if not known, insert it */
    2989          12 :   qs = insert_known_coin (cls,
    2990             :                           session,
    2991             :                           coin);
    2992          12 :   if (0 >= qs)
    2993             :   {
    2994           0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    2995           0 :       qs = GNUNET_DB_STATUS_HARD_ERROR; /* should be impossible */
    2996           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2997           0 :     return qs;
    2998             :   }
    2999          12 :   return qs;
    3000             : }
    3001             : 
    3002             : 
    3003             : /**
    3004             :  * Insert information about deposited coin into the database.
    3005             :  *
    3006             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3007             :  * @param session connection to the database
    3008             :  * @param deposit deposit information to store
    3009             :  * @return query result status
    3010             :  */
    3011             : static enum GNUNET_DB_QueryStatus
    3012          33 : postgres_insert_deposit (void *cls,
    3013             :                          struct TALER_EXCHANGEDB_Session *session,
    3014             :                          const struct TALER_EXCHANGEDB_Deposit *deposit)
    3015             : {
    3016             :   enum GNUNET_DB_QueryStatus qs;
    3017         330 :   struct GNUNET_PQ_QueryParam params[] = {
    3018          33 :     GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
    3019          33 :     TALER_PQ_query_param_amount (&deposit->amount_with_fee),
    3020          33 :     GNUNET_PQ_query_param_absolute_time (&deposit->timestamp),
    3021          33 :     GNUNET_PQ_query_param_absolute_time (&deposit->refund_deadline),
    3022          33 :     GNUNET_PQ_query_param_absolute_time (&deposit->wire_deadline),
    3023          33 :     GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
    3024          33 :     GNUNET_PQ_query_param_auto_from_type (&deposit->h_contract_terms),
    3025          33 :     GNUNET_PQ_query_param_auto_from_type (&deposit->h_wire),
    3026          33 :     GNUNET_PQ_query_param_auto_from_type (&deposit->csig),
    3027          33 :     TALER_PQ_query_param_json (deposit->receiver_wire_account),
    3028             :     GNUNET_PQ_query_param_end
    3029             :   };
    3030             : 
    3031          33 :   if (0 > (qs = ensure_coin_known (cls,
    3032             :                                    session,
    3033             :                                    &deposit->coin)))
    3034           0 :     return qs;
    3035          33 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3036             :               "Inserting deposit to be executed at %s (%llu/%llu)\n",
    3037             :               GNUNET_STRINGS_absolute_time_to_string (deposit->wire_deadline),
    3038             :               (unsigned long long) deposit->wire_deadline.abs_value_us,
    3039             :               (unsigned long long) deposit->refund_deadline.abs_value_us);
    3040          33 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    3041             :                                              "insert_deposit",
    3042             :                                              params);
    3043             : }
    3044             : 
    3045             : 
    3046             : /**
    3047             :  * Insert information about refunded coin into the database.
    3048             :  *
    3049             :  * @param cls the @e cls of this struct with the plugin-specific state
    3050             :  * @param session connection to the database
    3051             :  * @param refund refund information to store
    3052             :  * @return query result status
    3053             :  */
    3054             : static enum GNUNET_DB_QueryStatus
    3055           2 : postgres_insert_refund (void *cls,
    3056             :                         struct TALER_EXCHANGEDB_Session *session,
    3057             :                         const struct TALER_EXCHANGEDB_Refund *refund)
    3058             : {
    3059          12 :   struct GNUNET_PQ_QueryParam params[] = {
    3060           2 :     GNUNET_PQ_query_param_auto_from_type (&refund->coin.coin_pub),
    3061           2 :     GNUNET_PQ_query_param_auto_from_type (&refund->merchant_pub),
    3062           2 :     GNUNET_PQ_query_param_auto_from_type (&refund->merchant_sig),
    3063           2 :     GNUNET_PQ_query_param_auto_from_type (&refund->h_contract_terms),
    3064           2 :     GNUNET_PQ_query_param_uint64 (&refund->rtransaction_id),
    3065           2 :     TALER_PQ_query_param_amount (&refund->refund_amount),
    3066             :     GNUNET_PQ_query_param_end
    3067             :   };
    3068             : 
    3069           2 :   GNUNET_assert (GNUNET_YES ==
    3070             :                  TALER_amount_cmp_currency (&refund->refund_amount,
    3071             :                                             &refund->refund_fee));
    3072           2 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    3073             :                                              "insert_refund",
    3074             :                                              params);
    3075             : }
    3076             : 
    3077             : 
    3078             : /**
    3079             :  * Lookup refresh session data under the given @a session_hash.
    3080             :  *
    3081             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3082             :  * @param session database handle to use
    3083             :  * @param session_hash hash over the melt to use to locate the session
    3084             :  * @param[out] refresh_session where to store the result
    3085             :  * @return transaction status
    3086             :  */
    3087             : static enum GNUNET_DB_QueryStatus
    3088           5 : postgres_get_refresh_session (void *cls,
    3089             :                               struct TALER_EXCHANGEDB_Session *session,
    3090             :                               const struct GNUNET_HashCode *session_hash,
    3091             :                               struct TALER_EXCHANGEDB_RefreshSession *refresh_session)
    3092             : {
    3093           5 :   struct GNUNET_PQ_QueryParam params[] = {
    3094             :     GNUNET_PQ_query_param_auto_from_type (session_hash),
    3095             :     GNUNET_PQ_query_param_end
    3096             :   };
    3097          30 :   struct GNUNET_PQ_ResultSpec rs[] = {
    3098           5 :     GNUNET_PQ_result_spec_uint16 ("num_newcoins",
    3099             :                                   &refresh_session->num_newcoins),
    3100           5 :     GNUNET_PQ_result_spec_uint16 ("noreveal_index",
    3101             :                                   &refresh_session->noreveal_index),
    3102           5 :     GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
    3103             :                                           &refresh_session->melt.coin.coin_pub),
    3104           5 :     GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
    3105             :                                           &refresh_session->melt.coin_sig),
    3106           5 :     TALER_PQ_result_spec_amount ("amount_with_fee",
    3107             :                                  &refresh_session->melt.amount_with_fee),
    3108           5 :     TALER_PQ_result_spec_amount ("fee_refresh",
    3109             :                                  &refresh_session->melt.melt_fee),
    3110             :     GNUNET_PQ_result_spec_end
    3111             :   };
    3112             :   enum GNUNET_DB_QueryStatus qs;
    3113             : 
    3114           5 :   memset (refresh_session,
    3115             :           0,
    3116             :           sizeof (struct TALER_EXCHANGEDB_RefreshSession));
    3117           5 :   qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    3118             :                                                  "get_refresh_session",
    3119             :                                                  params,
    3120             :                                                  rs);
    3121           8 :   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
    3122             :        (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    3123           3 :         (qs = get_known_coin (cls,
    3124             :                               session,
    3125           3 :                               &refresh_session->melt.coin.coin_pub,
    3126             :                               &refresh_session->melt.coin)) ) )
    3127             :   {
    3128           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    3129           0 :     return qs;
    3130             :   }
    3131           5 :   refresh_session->melt.session_hash = *session_hash;
    3132           5 :   return qs;
    3133             : }
    3134             : 
    3135             : 
    3136             : /**
    3137             :  * Store new refresh session data under the given @a session_hash.
    3138             :  *
    3139             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3140             :  * @param session database handle to use
    3141             :  * @param session_hash hash over the melt to use to locate the session
    3142             :  * @param refresh_session session data to store
    3143             :  * @return query status for the transaction
    3144             :  */
    3145             : static enum GNUNET_DB_QueryStatus
    3146           2 : postgres_create_refresh_session (void *cls,
    3147             :                                  struct TALER_EXCHANGEDB_Session *session,
    3148             :                                  const struct GNUNET_HashCode *session_hash,
    3149             :                                  const struct TALER_EXCHANGEDB_RefreshSession *refresh_session)
    3150             : {
    3151          12 :   struct GNUNET_PQ_QueryParam params[] = {
    3152             :     GNUNET_PQ_query_param_auto_from_type (session_hash),
    3153           2 :     GNUNET_PQ_query_param_auto_from_type (&refresh_session->melt.coin.coin_pub),
    3154           2 :     GNUNET_PQ_query_param_auto_from_type (&refresh_session->melt.coin_sig),
    3155           2 :     TALER_PQ_query_param_amount (&refresh_session->melt.amount_with_fee),
    3156           2 :     GNUNET_PQ_query_param_uint16 (&refresh_session->num_newcoins),
    3157           2 :     GNUNET_PQ_query_param_uint16 (&refresh_session->noreveal_index),
    3158             :     GNUNET_PQ_query_param_end
    3159             :   };
    3160             :   enum GNUNET_DB_QueryStatus qs;
    3161             : 
    3162           2 :   if (0 > (qs = ensure_coin_known (cls,
    3163             :                                    session,
    3164             :                                    &refresh_session->melt.coin)))
    3165           0 :     return qs;
    3166           2 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    3167             :                                              "insert_refresh_session",
    3168             :                                              params);
    3169             : }
    3170             : 
    3171             : 
    3172             : /**
    3173             :  * Store in the database which coin(s) we want to create
    3174             :  * in a given refresh operation.
    3175             :  *
    3176             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3177             :  * @param session database connection
    3178             :  * @param session_hash hash to identify refresh session
    3179             :  * @param num_newcoins number of coins to generate, size of the @a denom_pubs array
    3180             :  * @param denom_pubs array denominations of the coins to create
    3181             :  * @return query status for the transaction
    3182             :  */
    3183             : static enum GNUNET_DB_QueryStatus
    3184           2 : postgres_insert_refresh_order (void *cls,
    3185             :                                struct TALER_EXCHANGEDB_Session *session,
    3186             :                                const struct GNUNET_HashCode *session_hash,
    3187             :                                uint16_t num_newcoins,
    3188             :                                const struct TALER_DenominationPublicKey *denom_pubs)
    3189             : {
    3190          24 :   for (unsigned int i=0;i<(unsigned int) num_newcoins;i++)
    3191             :   {
    3192          22 :     uint16_t newcoin_off = (uint16_t) i;
    3193             : 
    3194             :     {
    3195             :       struct GNUNET_HashCode denom_pub_hash;
    3196          22 :       struct GNUNET_PQ_QueryParam params[] = {
    3197             :         GNUNET_PQ_query_param_uint16 (&newcoin_off),
    3198             :         GNUNET_PQ_query_param_auto_from_type (session_hash),
    3199             :         GNUNET_PQ_query_param_auto_from_type (&denom_pub_hash),
    3200             :         GNUNET_PQ_query_param_end
    3201             :       };
    3202             :       enum GNUNET_DB_QueryStatus qs;
    3203             : 
    3204          22 :       GNUNET_CRYPTO_rsa_public_key_hash (denom_pubs[i].rsa_public_key,
    3205             :                                          &denom_pub_hash);
    3206          22 :       qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
    3207             :                                                "insert_refresh_order",
    3208             :                                                params);
    3209          22 :       if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    3210           0 :         return qs;
    3211             :     }
    3212             :   }
    3213           2 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    3214             : }
    3215             : 
    3216             : 
    3217             : /**
    3218             :  * We allocated some @a denom_pubs information, but now need
    3219             :  * to abort. Free allocated memory.
    3220             :  *
    3221             :  * @param denom_pubs data to free (but not the array itself)
    3222             :  * @param denom_pubs_len length of @a denom_pubs array
    3223             :  */
    3224             : static void
    3225           0 : free_dpk_result (struct TALER_DenominationPublicKey *denom_pubs,
    3226             :                  unsigned int denom_pubs_len)
    3227             : {
    3228           0 :   for (unsigned int i=0;i<denom_pubs_len;i++)
    3229             :   {
    3230           0 :     GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[i].rsa_public_key);
    3231           0 :     denom_pubs[i].rsa_public_key = NULL;
    3232             :   }
    3233           0 : }
    3234             : 
    3235             : 
    3236             : /**
    3237             :  * Lookup in the database the coins that we want to
    3238             :  * create in the given refresh operation.
    3239             :  *
    3240             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3241             :  * @param session database connection
    3242             :  * @param session_hash hash to identify refresh session
    3243             :  * @param num_newcoins size of the array of the @a denom_pubs array
    3244             :  * @param denom_pubs where to store the deomination keys
    3245             :  * @return transaction status
    3246             :  */
    3247             : static enum GNUNET_DB_QueryStatus
    3248           3 : postgres_get_refresh_order (void *cls,
    3249             :                             struct TALER_EXCHANGEDB_Session *session,
    3250             :                             const struct GNUNET_HashCode *session_hash,
    3251             :                             uint16_t num_newcoins,
    3252             :                             struct TALER_DenominationPublicKey *denom_pubs)
    3253             : { 
    3254          42 :   for (unsigned i=0;i<(unsigned int) num_newcoins;i++)
    3255             :   {
    3256          39 :     uint16_t newcoin_off = (uint16_t) i;
    3257             :     enum GNUNET_DB_QueryStatus qs;
    3258          39 :     struct GNUNET_PQ_QueryParam params[] = {
    3259             :       GNUNET_PQ_query_param_auto_from_type (session_hash),
    3260             :       GNUNET_PQ_query_param_uint16 (&newcoin_off),
    3261             :       GNUNET_PQ_query_param_end
    3262             :     };
    3263          39 :     struct GNUNET_PQ_ResultSpec rs[] = {
    3264          39 :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    3265          39 :                                             &denom_pubs[i].rsa_public_key),
    3266             :       GNUNET_PQ_result_spec_end
    3267             :     };
    3268             :     
    3269          39 :     qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    3270             :                                                    "get_refresh_order",
    3271             :                                                    params,
    3272             :                                                    rs);
    3273          39 :     switch (qs)
    3274             :     {
    3275             :     case GNUNET_DB_STATUS_HARD_ERROR:
    3276             :     case GNUNET_DB_STATUS_SOFT_ERROR:
    3277             :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    3278           0 :       free_dpk_result (denom_pubs, i);
    3279           0 :       return qs;
    3280             :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    3281          39 :       break;
    3282             :     default:
    3283           0 :       GNUNET_break (0);
    3284           0 :       break;
    3285             :     }
    3286             :   }
    3287           3 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    3288             : }
    3289             : 
    3290             : 
    3291             : /**
    3292             :  * Store information about the commitment of the
    3293             :  * given coin for the given refresh session in the database.
    3294             :  *
    3295             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3296             :  * @param session database connection to use
    3297             :  * @param session_hash hash to identify refresh session
    3298             :  * @param num_newcoins coin index size of the @a commit_coins array
    3299             :  * @param commit_coins array of coin commitments to store
    3300             :  * @return query transaction status
    3301             :  */
    3302             : static enum GNUNET_DB_QueryStatus
    3303           2 : postgres_insert_refresh_commit_coins (void *cls,
    3304             :                                       struct TALER_EXCHANGEDB_Session *session,
    3305             :                                       const struct GNUNET_HashCode *session_hash,
    3306             :                                       uint16_t num_newcoins,
    3307             :                                       const struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins)
    3308             : {
    3309          24 :   for (uint16_t coin_off=0;coin_off<num_newcoins;coin_off++)
    3310             :   {
    3311          44 :     struct GNUNET_PQ_QueryParam params[] = {
    3312             :       GNUNET_PQ_query_param_auto_from_type (session_hash),
    3313             :       GNUNET_PQ_query_param_uint16 (&coin_off),
    3314          22 :       GNUNET_PQ_query_param_fixed_size (commit_coins[coin_off].coin_ev,
    3315          22 :                                         commit_coins[coin_off].coin_ev_size),
    3316             :       GNUNET_PQ_query_param_end
    3317             :     };
    3318             :     enum GNUNET_DB_QueryStatus qs;
    3319             : 
    3320          22 :     qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
    3321             :                                              "insert_refresh_commit_coin",
    3322             :                                              params);
    3323          22 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    3324           0 :       return qs;
    3325             :   }
    3326           2 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    3327             : }
    3328             : 
    3329             : 
    3330             : /**
    3331             :  * We allocated some @a commit_coin information, but now need
    3332             :  * to abort. Free allocated memory.
    3333             :  *
    3334             :  * @param cls unused
    3335             :  * @param commit_coins_len length of @a commit_coins array
    3336             :  * @param commit_coins data to free (but not the array itself)
    3337             :  */
    3338             : static void
    3339           1 : postgres_free_refresh_commit_coins (void *cls,
    3340             :                                     unsigned int commit_coins_len,
    3341             :                                     struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins)
    3342             : {
    3343           6 :   for (unsigned int i=0;i<commit_coins_len;i++)
    3344             :   {
    3345           5 :     GNUNET_free (commit_coins[i].coin_ev);
    3346           5 :     commit_coins[i].coin_ev = NULL;
    3347           5 :     commit_coins[i].coin_ev_size = 0;
    3348             :   }
    3349           1 : }
    3350             : 
    3351             : 
    3352             : /**
    3353             :  * Obtain information about the commitment of the
    3354             :  * given coin of the given refresh session from the database.
    3355             :  *
    3356             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3357             :  * @param session database connection to use
    3358             :  * @param session_hash hash to identify refresh session
    3359             :  * @param num_newcoins size of the @a commit_coins array
    3360             :  * @param[out] commit_coins array of coin commitments to return
    3361             :  * @return transaction status
    3362             :  */
    3363             : static enum GNUNET_DB_QueryStatus
    3364           3 : postgres_get_refresh_commit_coins (void *cls,
    3365             :                                    struct TALER_EXCHANGEDB_Session *session,
    3366             :                                    const struct GNUNET_HashCode *session_hash,
    3367             :                                    uint16_t num_newcoins,
    3368             :                                    struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins)
    3369             : {
    3370          42 :   for (unsigned int i=0;i<(unsigned int) num_newcoins;i++)
    3371             :   {
    3372          39 :     uint16_t newcoin_off = (uint16_t) i;
    3373          39 :     struct GNUNET_PQ_QueryParam params[] = {
    3374             :       GNUNET_PQ_query_param_auto_from_type (session_hash),
    3375             :       GNUNET_PQ_query_param_uint16 (&newcoin_off),
    3376             :       GNUNET_PQ_query_param_end
    3377             :     };
    3378             :     void *c_buf;
    3379             :     size_t c_buf_size;
    3380          39 :     struct GNUNET_PQ_ResultSpec rs[] = {
    3381             :       GNUNET_PQ_result_spec_variable_size ("coin_ev",
    3382             :                                            &c_buf,
    3383             :                                            &c_buf_size),
    3384             :       GNUNET_PQ_result_spec_end
    3385             :     };
    3386             :     enum GNUNET_DB_QueryStatus qs;
    3387             : 
    3388          39 :     qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    3389             :                                                    "get_refresh_commit_coin",
    3390             :                                                    params,
    3391             :                                                    rs);
    3392          39 :     if (0 >= qs)
    3393             :     {
    3394           0 :       postgres_free_refresh_commit_coins (cls,
    3395             :                                           i,
    3396             :                                           commit_coins);
    3397           0 :       return qs;
    3398             :     }
    3399          39 :     commit_coins[i].coin_ev = c_buf;
    3400          39 :     commit_coins[i].coin_ev_size = c_buf_size;
    3401             :   }
    3402           3 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    3403             : }
    3404             : 
    3405             : 
    3406             : /**
    3407             :  * Store the commitment to the given (encrypted) refresh link data
    3408             :  * for the given refresh session.
    3409             :  *
    3410             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3411             :  * @param session database connection to use
    3412             :  * @param session_hash hash to identify refresh session
    3413             :  * @param tp transfer public key to store
    3414             :  * @return transaction status
    3415             :  */
    3416             : static enum GNUNET_DB_QueryStatus
    3417           2 : postgres_insert_refresh_transfer_public_key (void *cls,
    3418             :                                              struct TALER_EXCHANGEDB_Session *session,
    3419             :                                              const struct GNUNET_HashCode *session_hash,
    3420             :                                              const struct TALER_TransferPublicKeyP *tp)
    3421             : {
    3422           2 :   struct GNUNET_PQ_QueryParam params[] = {
    3423             :     GNUNET_PQ_query_param_auto_from_type (session_hash),
    3424             :     GNUNET_PQ_query_param_auto_from_type (tp),
    3425             :     GNUNET_PQ_query_param_end
    3426             :   };
    3427             : 
    3428           2 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    3429             :                                              "insert_transfer_public_key",
    3430             :                                              params);
    3431             : }
    3432             : 
    3433             : 
    3434             : /**
    3435             :  * Obtain the commited (encrypted) refresh link data
    3436             :  * for the given refresh session.
    3437             :  *
    3438             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3439             :  * @param session database connection to use
    3440             :  * @param session_hash hash to identify refresh session
    3441             :  * @param[out] tp information to return
    3442             :  * @return transaction status
    3443             :  */
    3444             : static enum GNUNET_DB_QueryStatus
    3445           4 : postgres_get_refresh_transfer_public_key (void *cls,
    3446             :                                           struct TALER_EXCHANGEDB_Session *session,
    3447             :                                           const struct GNUNET_HashCode *session_hash,
    3448             :                                           struct TALER_TransferPublicKeyP *tp)
    3449             : {
    3450           4 :   struct GNUNET_PQ_QueryParam params[] = {
    3451             :     GNUNET_PQ_query_param_auto_from_type (session_hash),
    3452             :     GNUNET_PQ_query_param_end
    3453             :   };
    3454           4 :   struct GNUNET_PQ_ResultSpec rs[] = {
    3455             :     GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
    3456             :                                           tp),
    3457             :     GNUNET_PQ_result_spec_end
    3458             :   };
    3459             :   
    3460           4 :   return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    3461             :                                                    "get_refresh_transfer_public_key",
    3462             :                                                    params,
    3463             :                                                    rs);
    3464             : }
    3465             : 
    3466             : 
    3467             : /**
    3468             :  * Get signature of a new coin generated during refresh into
    3469             :  * the database indexed by the refresh session and the index
    3470             :  * of the coin.
    3471             :  *
    3472             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3473             :  * @param session database connection
    3474             :  * @param session_hash hash to identify refresh session
    3475             :  * @param newcoin_index coin index
    3476             :  * @param ev_sig coin signature
    3477             :  * @return transaction result status
    3478             :  */
    3479             : static enum GNUNET_DB_QueryStatus
    3480          44 : postgres_get_refresh_out (void *cls,
    3481             :                           struct TALER_EXCHANGEDB_Session *session,
    3482             :                           const struct GNUNET_HashCode *session_hash,
    3483             :                           uint16_t newcoin_index,
    3484             :                           struct TALER_DenominationSignature *ev_sig)
    3485             : {
    3486          44 :   struct GNUNET_PQ_QueryParam params[] = {
    3487             :     GNUNET_PQ_query_param_auto_from_type (session_hash),
    3488             :     GNUNET_PQ_query_param_uint16 (&newcoin_index),
    3489             :     GNUNET_PQ_query_param_end
    3490             :   };
    3491          44 :   struct GNUNET_PQ_ResultSpec rs[] = {
    3492          44 :     GNUNET_PQ_result_spec_rsa_signature ("ev_sig",
    3493             :                                          &ev_sig->rsa_signature),
    3494             :     GNUNET_PQ_result_spec_end
    3495             :   };
    3496             : 
    3497          44 :   return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    3498             :                                                    "get_refresh_out",
    3499             :                                                    params,
    3500             :                                                    rs);
    3501             : }
    3502             : 
    3503             : 
    3504             : /**
    3505             :  * Insert signature of a new coin generated during refresh into
    3506             :  * the database indexed by the refresh session and the index
    3507             :  * of the coin.  This data is later used should an old coin
    3508             :  * be used to try to obtain the private keys during "/refresh/link".
    3509             :  *
    3510             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3511             :  * @param session database connection
    3512             :  * @param session_hash hash to identify refresh session
    3513             :  * @param newcoin_index coin index
    3514             :  * @param ev_sig coin signature
    3515             :  * @return transaction result status
    3516             :  */
    3517             : static enum GNUNET_DB_QueryStatus
    3518          22 : postgres_insert_refresh_out (void *cls,
    3519             :                              struct TALER_EXCHANGEDB_Session *session,
    3520             :                              const struct GNUNET_HashCode *session_hash,
    3521             :                              uint16_t newcoin_index,
    3522             :                              const struct TALER_DenominationSignature *ev_sig)
    3523             : {
    3524          44 :   struct GNUNET_PQ_QueryParam params[] = {
    3525             :     GNUNET_PQ_query_param_auto_from_type (session_hash),
    3526             :     GNUNET_PQ_query_param_uint16 (&newcoin_index),
    3527          22 :     GNUNET_PQ_query_param_rsa_signature (ev_sig->rsa_signature),
    3528             :     GNUNET_PQ_query_param_end
    3529             :   };
    3530             : 
    3531          22 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    3532             :                                              "insert_refresh_out",
    3533             :                                              params);
    3534             : }
    3535             : 
    3536             : 
    3537             : /**
    3538             :  * Closure for #add_ldl().
    3539             :  */
    3540             : struct LinkDataContext
    3541             : {
    3542             :   /** 
    3543             :    * List we are building.
    3544             :    */ 
    3545             :   struct TALER_EXCHANGEDB_LinkDataList *ldl;
    3546             : 
    3547             :   /**
    3548             :    * Status, set to #GNUNET_SYSERR on errors,
    3549             :    */
    3550             :   int status;
    3551             : };
    3552             : 
    3553             : 
    3554             : /**
    3555             :  * Function to be called with the results of a SELECT statement
    3556             :  * that has returned @a num_results results.
    3557             :  *
    3558             :  * @param cls closure of type `struct LinkDataContext *`
    3559             :  * @param result the postgres result
    3560             :  * @param num_result the number of results in @a result
    3561             :  */
    3562             : static void
    3563           2 : add_ldl (void *cls,
    3564             :          PGresult *result,
    3565             :          unsigned int num_results)
    3566             : {
    3567           2 :   struct LinkDataContext *ldc = cls;
    3568             :   
    3569          24 :   for (int i = num_results - 1; i >= 0; i--)
    3570             :   {
    3571             :     struct GNUNET_CRYPTO_RsaPublicKey *denom_pub;
    3572             :     struct GNUNET_CRYPTO_RsaSignature *sig;
    3573             :     struct TALER_EXCHANGEDB_LinkDataList *pos;
    3574             : 
    3575          22 :     pos = GNUNET_new (struct TALER_EXCHANGEDB_LinkDataList);
    3576             :     {
    3577          22 :       struct GNUNET_PQ_ResultSpec rs[] = {
    3578             :         GNUNET_PQ_result_spec_rsa_signature ("ev_sig",
    3579             :                                              &sig),
    3580             :         GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    3581             :                                               &denom_pub),
    3582             :         GNUNET_PQ_result_spec_end
    3583             :       };
    3584             : 
    3585          22 :       if (GNUNET_OK !=
    3586          22 :           GNUNET_PQ_extract_result (result,
    3587             :                                     rs,
    3588             :                                     i))
    3589             :       {
    3590           0 :         GNUNET_break (0);
    3591           0 :         common_free_link_data_list (cls,
    3592             :                                     ldc->ldl);
    3593           0 :         ldc->ldl = NULL;
    3594           0 :         GNUNET_free (pos);
    3595           0 :         ldc->status = GNUNET_SYSERR;
    3596           0 :         return;
    3597             :       }
    3598             :     }
    3599          22 :     pos->next = ldc->ldl;
    3600          22 :     pos->denom_pub.rsa_public_key = denom_pub;
    3601          22 :     pos->ev_sig.rsa_signature = sig;
    3602          22 :     ldc->ldl = pos;
    3603             :   }
    3604             : }
    3605             : 
    3606             : 
    3607             : /**
    3608             :  * Obtain the link data of a coin, that is the encrypted link
    3609             :  * information, the denomination keys and the signatures.
    3610             :  *
    3611             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3612             :  * @param session database connection
    3613             :  * @param session_hash refresh session to get linkage data for
    3614             :  * @param[out] ldlp set to all known link data for the session
    3615             :  * @return transaction status code
    3616             :  */
    3617             : static enum GNUNET_DB_QueryStatus
    3618           2 : postgres_get_link_data_list (void *cls,
    3619             :                              struct TALER_EXCHANGEDB_Session *session,
    3620             :                              const struct GNUNET_HashCode *session_hash,
    3621             :                              struct TALER_EXCHANGEDB_LinkDataList **ldlp)
    3622             : {
    3623             :   struct LinkDataContext ldc;
    3624           2 :   struct GNUNET_PQ_QueryParam params[] = {
    3625             :     GNUNET_PQ_query_param_auto_from_type (session_hash),
    3626             :     GNUNET_PQ_query_param_end
    3627             :   };
    3628             :   enum GNUNET_DB_QueryStatus qs;
    3629             : 
    3630           2 :   ldc.status = GNUNET_OK;
    3631           2 :   ldc.ldl = NULL;
    3632           2 :   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    3633             :                                              "get_link",
    3634             :                                              params,
    3635             :                                              &add_ldl,
    3636             :                                              &ldc);
    3637           2 :   *ldlp = ldc.ldl;
    3638           2 :   if (GNUNET_OK != ldc.status)
    3639           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    3640           2 :   return qs;
    3641             : }
    3642             : 
    3643             : 
    3644             : /**
    3645             :  * Closure for #add_link().
    3646             :  */
    3647             : struct AddLinkContext
    3648             : {
    3649             :   /**
    3650             :    * Function to call on each result.
    3651             :    */
    3652             :   TALER_EXCHANGEDB_TransferDataCallback tdc;
    3653             : 
    3654             :   /**
    3655             :    * Closure for @e tdc.
    3656             :    */
    3657             :   void *tdc_cls;
    3658             : 
    3659             :   /**
    3660             :    * Status code, set to #GNUNET_SYSERR on errors.
    3661             :    */
    3662             :   int status;
    3663             : };
    3664             : 
    3665             : 
    3666             : /**
    3667             :  * Function to be called with the results of a SELECT statement
    3668             :  * that has returned @a num_results results.
    3669             :  *
    3670             :  * @param cls closure of type `struct AddLinkContext *`
    3671             :  * @param result the postgres result
    3672             :  * @param num_result the number of results in @a result
    3673             :  */
    3674             : static void
    3675           2 : add_link (void *cls,
    3676             :           PGresult *result,
    3677             :           unsigned int num_results)
    3678             : {
    3679           2 :   struct AddLinkContext *alc = cls;
    3680             : 
    3681           4 :   for (unsigned int i=0;i<num_results;i++)
    3682             :   {
    3683             :     struct GNUNET_HashCode session_hash;
    3684             :     struct TALER_TransferPublicKeyP transfer_pub;
    3685           2 :     struct GNUNET_PQ_ResultSpec rs[] = {
    3686             :       GNUNET_PQ_result_spec_auto_from_type ("transfer_pub", &transfer_pub),
    3687             :       GNUNET_PQ_result_spec_auto_from_type ("session_hash", &session_hash),
    3688             :       GNUNET_PQ_result_spec_end
    3689             :     };
    3690             : 
    3691           2 :     if (GNUNET_OK !=
    3692           2 :         GNUNET_PQ_extract_result (result,
    3693             :                                   rs,
    3694             :                                   i))
    3695             :     {
    3696           0 :       GNUNET_break (0);
    3697           0 :       alc->status = GNUNET_SYSERR;
    3698           0 :       return;
    3699             :     }
    3700           2 :     alc->tdc (alc->tdc_cls,
    3701             :               &session_hash,
    3702             :               &transfer_pub);
    3703             :   }
    3704             : }
    3705             : 
    3706             : 
    3707             : /**
    3708             :  * Obtain shared secret and transfer public key from the public key of
    3709             :  * the coin.  This information and the link information returned by
    3710             :  * #postgres_get_link_data_list() enable the owner of an old coin to
    3711             :  * determine the private keys of the new coins after the melt.
    3712             :  *
    3713             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    3714             :  * @param session database connection
    3715             :  * @param coin_pub public key of the coin
    3716             :  * @param tdc function to call for each session the coin was melted into
    3717             :  * @param tdc_cls closure for @a tdc
    3718             :  * @return statement execution status
    3719             :  */
    3720             : static enum GNUNET_DB_QueryStatus
    3721           2 : postgres_get_transfer (void *cls,
    3722             :                        struct TALER_EXCHANGEDB_Session *session,
    3723             :                        const struct TALER_CoinSpendPublicKeyP *coin_pub,
    3724             :                        TALER_EXCHANGEDB_TransferDataCallback tdc,
    3725             :                        void *tdc_cls)
    3726             : {
    3727           2 :   struct GNUNET_PQ_QueryParam params[] = {
    3728             :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    3729             :     GNUNET_PQ_query_param_end
    3730             :   };
    3731             :   struct AddLinkContext al_ctx;
    3732             :   enum GNUNET_DB_QueryStatus qs;
    3733             : 
    3734           2 :   al_ctx.tdc = tdc;
    3735           2 :   al_ctx.tdc_cls = tdc_cls;
    3736           2 :   al_ctx.status = GNUNET_OK;
    3737           2 :   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    3738             :                                              "get_transfer",
    3739             :                                              params,
    3740             :                                              &add_link,
    3741             :                                              &al_ctx);
    3742           2 :   if (GNUNET_OK != al_ctx.status)
    3743           0 :     qs = GNUNET_DB_STATUS_HARD_ERROR;
    3744           2 :   return qs;
    3745             : }
    3746             : 
    3747             : 
    3748             : /**
    3749             :  * Closure for callbacks called from #postgres_get_coin_transactions()
    3750             :  */
    3751             : struct CoinHistoryContext
    3752             : {
    3753             :   /**
    3754             :    * Head of the coin's history list.
    3755             :    */
    3756             :   struct TALER_EXCHANGEDB_TransactionList *head;
    3757             : 
    3758             :   /**
    3759             :    * Public key of the coin we are building the history for.
    3760             :    */
    3761             :   const struct TALER_CoinSpendPublicKeyP *coin_pub;
    3762             : 
    3763             :   /**
    3764             :    * Closure for all callbacks of this database plugin.
    3765             :    */
    3766             :   void *db_cls;
    3767             : 
    3768             :   /**
    3769             :    * Database session we are using.
    3770             :    */
    3771             :   struct TALER_EXCHANGEDB_Session *session;
    3772             :   
    3773             :   /**
    3774             :    * Set to transaction status.
    3775             :    */ 
    3776             :   enum GNUNET_DB_QueryStatus status;
    3777             : };
    3778             : 
    3779             : 
    3780             : /**
    3781             :  * Function to be called with the results of a SELECT statement
    3782             :  * that has returned @a num_results results.
    3783             :  *
    3784             :  * @param cls closure of type `struct CoinHistoryContext`
    3785             :  * @param result the postgres result
    3786             :  * @param num_result the number of results in @a result
    3787             :  */
    3788             : static void
    3789          19 : add_coin_deposit (void *cls,
    3790             :                   PGresult *result,
    3791             :                   unsigned int num_results)
    3792             : {
    3793          19 :   struct CoinHistoryContext *chc = cls;
    3794             : 
    3795          31 :   for (unsigned int i = 0; i < num_results; i++)
    3796             :   {
    3797             :     struct TALER_EXCHANGEDB_Deposit *deposit;
    3798             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    3799             :     enum GNUNET_DB_QueryStatus qs;
    3800             :     
    3801          12 :     deposit = GNUNET_new (struct TALER_EXCHANGEDB_Deposit);
    3802             :     {
    3803         108 :       struct GNUNET_PQ_ResultSpec rs[] = {
    3804          12 :         TALER_PQ_result_spec_amount ("amount_with_fee",
    3805             :                                      &deposit->amount_with_fee),
    3806          12 :         TALER_PQ_result_spec_amount ("fee_deposit",
    3807             :                                      &deposit->deposit_fee),
    3808          12 :         GNUNET_PQ_result_spec_absolute_time ("timestamp",
    3809             :                                              &deposit->timestamp),
    3810          12 :         GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
    3811             :                                              &deposit->refund_deadline),
    3812          12 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    3813             :                                               &deposit->merchant_pub),
    3814          12 :         GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    3815             :                                               &deposit->h_contract_terms),
    3816          12 :         GNUNET_PQ_result_spec_auto_from_type ("h_wire",
    3817             :                                               &deposit->h_wire),
    3818          12 :         TALER_PQ_result_spec_json ("wire",
    3819             :                                    &deposit->receiver_wire_account),
    3820          12 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    3821             :                                               &deposit->csig),
    3822             :         GNUNET_PQ_result_spec_end
    3823             :       };
    3824             :       
    3825          12 :       if (GNUNET_OK !=
    3826          12 :           GNUNET_PQ_extract_result (result,
    3827             :                                     rs,
    3828             :                                     i))
    3829             :       {
    3830           0 :         GNUNET_break (0);
    3831           0 :         GNUNET_free (deposit);
    3832           0 :         chc->status = GNUNET_DB_STATUS_HARD_ERROR;
    3833           0 :         return;
    3834             :       }
    3835          12 :       deposit->coin.coin_pub = *chc->coin_pub;
    3836             :     }
    3837          12 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    3838          12 :     tl->next = chc->head;
    3839          12 :     tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
    3840          12 :     tl->details.deposit = deposit;
    3841          12 :     qs = get_known_coin (chc->db_cls,
    3842             :                          chc->session,
    3843             :                          chc->coin_pub,
    3844             :                          &deposit->coin);
    3845          12 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    3846             :     {
    3847           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    3848           0 :       GNUNET_free (deposit);
    3849           0 :       chc->status = qs;
    3850           0 :       return;
    3851             :     }
    3852          12 :     chc->head = tl;
    3853             :   }
    3854             : }
    3855             : 
    3856             : 
    3857             : /**
    3858             :  * Function to be called with the results of a SELECT statement
    3859             :  * that has returned @a num_results results.
    3860             :  *
    3861             :  * @param cls closure of type `struct CoinHistoryContext`
    3862             :  * @param result the postgres result
    3863             :  * @param num_result the number of results in @a result
    3864             :  */
    3865             : static void
    3866          19 : add_coin_melt (void *cls,
    3867             :                PGresult *result,
    3868             :                unsigned int num_results)
    3869             : {
    3870          19 :   struct CoinHistoryContext *chc = cls;
    3871             : 
    3872          21 :   for (unsigned int i=0;i<num_results;i++)
    3873             :   {
    3874             :     struct TALER_EXCHANGEDB_RefreshMelt *melt;
    3875             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    3876             :     enum GNUNET_DB_QueryStatus qs;
    3877             : 
    3878           2 :     melt = GNUNET_new (struct TALER_EXCHANGEDB_RefreshMelt);
    3879             :     {
    3880           8 :       struct GNUNET_PQ_ResultSpec rs[] = {
    3881           2 :         GNUNET_PQ_result_spec_auto_from_type ("session_hash",
    3882             :                                               &melt->session_hash),
    3883             :         /* oldcoin_index not needed */
    3884           2 :         GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
    3885             :                                               &melt->coin_sig),
    3886           2 :         TALER_PQ_result_spec_amount ("amount_with_fee",
    3887             :                                      &melt->amount_with_fee),
    3888           2 :         TALER_PQ_result_spec_amount ("fee_refresh",
    3889             :                                      &melt->melt_fee),
    3890             :         GNUNET_PQ_result_spec_end
    3891             :       };
    3892             :       
    3893           2 :       if (GNUNET_OK !=
    3894           2 :           GNUNET_PQ_extract_result (result,
    3895             :                                     rs,
    3896             :                                     i))
    3897             :       {
    3898           0 :         GNUNET_break (0);
    3899           0 :         GNUNET_free (melt);
    3900           0 :         chc->status = GNUNET_DB_STATUS_HARD_ERROR;
    3901           0 :         return;
    3902             :       }
    3903           2 :       melt->coin.coin_pub = *chc->coin_pub;
    3904             :     }
    3905           2 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    3906           2 :     tl->next = chc->head;
    3907           2 :     tl->type = TALER_EXCHANGEDB_TT_REFRESH_MELT;
    3908           2 :     tl->details.melt = melt;
    3909           2 :     qs = get_known_coin (chc->db_cls,
    3910             :                          chc->session,
    3911             :                          chc->coin_pub,
    3912             :                          &melt->coin);
    3913           2 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    3914             :     {
    3915           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    3916           0 :       GNUNET_free (melt);
    3917           0 :       chc->status = qs;
    3918           0 :       return;
    3919             :     }
    3920           2 :     chc->head = tl;
    3921             :   }
    3922             : }
    3923             : 
    3924             : 
    3925             : /**
    3926             :  * Function to be called with the results of a SELECT statement
    3927             :  * that has returned @a num_results results.
    3928             :  *
    3929             :  * @param cls closure of type `struct CoinHistoryContext`
    3930             :  * @param result the postgres result
    3931             :  * @param num_result the number of results in @a result
    3932             :  */
    3933             : static void
    3934          19 : add_coin_refund (void *cls,
    3935             :                  PGresult *result,
    3936             :                  unsigned int num_results)
    3937             : {
    3938          19 :   struct CoinHistoryContext *chc = cls;
    3939             : 
    3940          22 :   for (unsigned int i=0;i<num_results;i++)
    3941             :   {
    3942             :     struct TALER_EXCHANGEDB_Refund *refund;
    3943             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    3944             :     enum GNUNET_DB_QueryStatus qs;
    3945             : 
    3946           3 :     refund = GNUNET_new (struct TALER_EXCHANGEDB_Refund);
    3947             :     {
    3948          18 :       struct GNUNET_PQ_ResultSpec rs[] = {
    3949           3 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    3950             :                                               &refund->merchant_pub),
    3951           3 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
    3952             :                                               &refund->merchant_sig),
    3953           3 :         GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    3954             :                                               &refund->h_contract_terms),
    3955           3 :         GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
    3956             :                                       &refund->rtransaction_id),
    3957           3 :         TALER_PQ_result_spec_amount ("amount_with_fee",
    3958             :                                      &refund->refund_amount),
    3959           3 :         TALER_PQ_result_spec_amount ("fee_refund",
    3960             :                                      &refund->refund_fee),
    3961             :         GNUNET_PQ_result_spec_end
    3962             :       };
    3963             :       
    3964           3 :       if (GNUNET_OK !=
    3965           3 :           GNUNET_PQ_extract_result (result,
    3966             :                                     rs,
    3967             :                                     i))
    3968             :       {
    3969           0 :         GNUNET_break (0);
    3970           0 :         GNUNET_free (refund);
    3971           0 :         chc->status = GNUNET_DB_STATUS_HARD_ERROR;
    3972           0 :         return;
    3973             :       }
    3974           3 :       refund->coin.coin_pub = *chc->coin_pub;
    3975             :     }
    3976           3 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    3977           3 :     tl->next = chc->head;
    3978           3 :     tl->type = TALER_EXCHANGEDB_TT_REFUND;
    3979           3 :     tl->details.refund = refund;
    3980           3 :     qs = get_known_coin (chc->db_cls,
    3981             :                          chc->session,
    3982             :                          chc->coin_pub,
    3983             :                          &refund->coin);
    3984           3 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    3985             :     {
    3986           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    3987           0 :       GNUNET_free (refund);
    3988           0 :       chc->status = qs;
    3989           0 :       return;
    3990             :     }
    3991           3 :     chc->head = tl;
    3992             :   }
    3993             : }
    3994             : 
    3995             : 
    3996             : /**
    3997             :  * Function to be called with the results of a SELECT statement
    3998             :  * that has returned @a num_results results.
    3999             :  *
    4000             :  * @param cls closure of type `struct CoinHistoryContext`
    4001             :  * @param result the postgres result
    4002             :  * @param num_result the number of results in @a result
    4003             :  */
    4004             : static void
    4005          19 : add_coin_payback (void *cls,
    4006             :                   PGresult *result,
    4007             :                   unsigned int num_results)
    4008             : {
    4009          19 :   struct CoinHistoryContext *chc = cls;
    4010             : 
    4011          21 :   for (unsigned int i=0;i<num_results;i++)
    4012             :   {
    4013             :     struct TALER_EXCHANGEDB_Payback *payback;
    4014             :     struct TALER_EXCHANGEDB_TransactionList *tl;
    4015             :     
    4016           2 :     payback = GNUNET_new (struct TALER_EXCHANGEDB_Payback);
    4017             :     {
    4018          14 :       struct GNUNET_PQ_ResultSpec rs[] = {
    4019           2 :         TALER_PQ_result_spec_amount ("amount",
    4020             :                                      &payback->value),
    4021           2 :         GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    4022             :                                               &payback->reserve_pub),
    4023           2 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
    4024             :                                               &payback->coin_blind),
    4025           2 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    4026             :                                               &payback->coin_sig),
    4027           2 :         GNUNET_PQ_result_spec_absolute_time ("timestamp",
    4028             :                                              &payback->timestamp),
    4029           2 :         GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    4030             :                                               &payback->coin.denom_pub.rsa_public_key),
    4031           2 :         GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    4032             :                                              &payback->coin.denom_sig.rsa_signature),
    4033             :         GNUNET_PQ_result_spec_end
    4034             :       };
    4035             :       
    4036           2 :       if (GNUNET_OK !=
    4037           2 :           GNUNET_PQ_extract_result (result,
    4038             :                                     rs,
    4039             :                                     i))
    4040             :       {
    4041           0 :         GNUNET_break (0);
    4042           0 :         GNUNET_free (payback);
    4043           0 :         chc->status = GNUNET_DB_STATUS_HARD_ERROR;
    4044           0 :         return;
    4045             :       }
    4046           2 :       payback->coin.coin_pub = *chc->coin_pub;
    4047             :     }
    4048           2 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
    4049           2 :     tl->next = chc->head;
    4050           2 :     tl->type = TALER_EXCHANGEDB_TT_PAYBACK;
    4051           2 :     tl->details.payback = payback;
    4052           2 :     chc->head = tl;
    4053             :   }
    4054             : }
    4055             : 
    4056             : 
    4057             : /**
    4058             :  * Compile a list of all (historic) transactions performed
    4059             :  * with the given coin (/refresh/melt, /deposit and /refund operations).
    4060             :  *
    4061             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
    4062             :  * @param session database connection
    4063             :  * @param coin_pub coin to investigate
    4064             :  * @param[out] tlp set to list of transactions, NULL if coin is fresh
    4065             :  * @return database transaction status
    4066             :  */
    4067             : static enum GNUNET_DB_QueryStatus
    4068          19 : postgres_get_coin_transactions (void *cls,
    4069             :                                 struct TALER_EXCHANGEDB_Session *session,
    4070             :                                 const struct TALER_CoinSpendPublicKeyP *coin_pub,
    4071             :                                 struct TALER_EXCHANGEDB_TransactionList **tlp)
    4072             : {
    4073             :   struct CoinHistoryContext chc;
    4074          19 :   struct GNUNET_PQ_QueryParam params[] = {
    4075             :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    4076             :     GNUNET_PQ_query_param_end
    4077             :   };
    4078             :   enum GNUNET_DB_QueryStatus qs;
    4079             :   struct {
    4080             :     /**
    4081             :      * SQL prepared statement name.
    4082             :      */
    4083             :     const char *statement;
    4084             : 
    4085             :     /**
    4086             :      * Function to call to handle the result(s).
    4087             :      */
    4088             :     GNUNET_PQ_PostgresResultHandler cb;
    4089          19 :   } work[] = {
    4090             :     /** #TALER_EXCHANGEDB_TT_DEPOSIT */
    4091             :     { "get_deposit_with_coin_pub",
    4092             :       &add_coin_deposit },
    4093             :     /** #TALER_EXCHANGEDB_TT_REFRESH_MELT */
    4094             :     { "get_refresh_session_by_coin",
    4095             :       &add_coin_melt },
    4096             :     /** #TALER_EXCHANGEDB_TT_REFUND */
    4097             :     { "get_refunds_by_coin",
    4098             :       &add_coin_refund },
    4099             :     /** #TALER_EXCHANGEDB_TT_PAYBACK */
    4100             :     { "payback_by_coin",
    4101             :       &add_coin_payback },
    4102             :     { NULL, NULL }
    4103             :   };
    4104             : 
    4105          19 :   chc.head = NULL;
    4106          19 :   chc.status = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    4107          19 :   chc.coin_pub = coin_pub;
    4108          19 :   chc.session = session;
    4109          19 :   chc.db_cls = cls;
    4110          95 :   for (unsigned int i=0;NULL != work[i].statement; i++)
    4111             :   {
    4112          76 :     qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    4113             :                                                work[i].statement,
    4114             :                                                params,
    4115             :                                                work[i].cb,
    4116             :                                                &chc);
    4117         152 :     if ( (0 > qs) ||
    4118          76 :          (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != chc.status) )
    4119             :     {
    4120           0 :       if (NULL != chc.head)
    4121           0 :         common_free_coin_transaction_list (cls,
    4122             :                                            chc.head);
    4123           0 :       *tlp = NULL;
    4124           0 :       if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != chc.status)
    4125           0 :         qs = chc.status;
    4126           0 :       return qs;
    4127             :     }
    4128             :   }
    4129          19 :   *tlp = chc.head;
    4130          19 :   if (NULL == chc.head)
    4131           7 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    4132          12 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    4133             : }
    4134             : 
    4135             : 
    4136             : /**
    4137             :  * Closure for #handle_wt_result.
    4138             :  */
    4139             : struct WireTransferResultContext
    4140             : {
    4141             :   /**
    4142             :    * Function to call on each result.
    4143             :    */
    4144             :   TALER_EXCHANGEDB_WireTransferDataCallback cb;
    4145             : 
    4146             :   /**
    4147             :    * Closure for @e cb.
    4148             :    */
    4149             :   void *cb_cls;
    4150             : 
    4151             :   /**
    4152             :    * Set to #GNUNET_SYSERR on serious errors.
    4153             :    */
    4154             :   int status;
    4155             : };
    4156             : 
    4157             : 
    4158             : /**
    4159             :  * Function to be called with the results of a SELECT statement
    4160             :  * that has returned @a num_results results.  Helper function
    4161             :  * for #postgres_lookup_wire_transfer().
    4162             :  *
    4163             :  * @param cls closure of type `struct WireTransferResultContext *`
    4164             :  * @param result the postgres result
    4165             :  * @param num_result the number of results in @a result
    4166             :  */
    4167             : static void
    4168           5 : handle_wt_result (void *cls,
    4169             :                   PGresult *result,
    4170             :                   unsigned int num_results)
    4171             : {
    4172           5 :   struct WireTransferResultContext *ctx = cls;
    4173             :   
    4174          16 :   for (unsigned int i=0;i<num_results;i++)
    4175             :   {
    4176             :     uint64_t rowid;
    4177             :     struct GNUNET_HashCode h_contract_terms;
    4178             :     struct GNUNET_HashCode h_wire;
    4179             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    4180             :     struct TALER_MerchantPublicKeyP merchant_pub;
    4181             :     struct GNUNET_TIME_Absolute exec_time;
    4182             :     struct TALER_Amount amount_with_fee;
    4183             :     struct TALER_Amount deposit_fee;
    4184             :     json_t *wire;
    4185             :     json_t *t;
    4186             :     const char *wire_method;
    4187           3 :     struct GNUNET_PQ_ResultSpec rs[] = {
    4188             :       GNUNET_PQ_result_spec_uint64 ("aggregation_serial_id", &rowid),
    4189             :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", &h_contract_terms),
    4190             :       TALER_PQ_result_spec_json ("wire", &wire),
    4191             :       GNUNET_PQ_result_spec_auto_from_type ("h_wire", &h_wire),
    4192             :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub", &coin_pub),
    4193             :       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub", &merchant_pub),
    4194             :       GNUNET_PQ_result_spec_absolute_time ("execution_date", &exec_time),
    4195             :       TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee),
    4196             :       TALER_PQ_result_spec_amount ("fee_deposit", &deposit_fee),
    4197             :       GNUNET_PQ_result_spec_end
    4198             :     };
    4199             : 
    4200           3 :     if (GNUNET_OK !=
    4201           3 :         GNUNET_PQ_extract_result (result,
    4202             :                                   rs,
    4203             :                                   i))
    4204             :     {
    4205           0 :       GNUNET_break (0);
    4206           0 :       ctx->status = GNUNET_SYSERR;
    4207           0 :       return;
    4208             :     }
    4209           3 :     t = json_object_get (wire, "type");
    4210           3 :     if (NULL == t)
    4211             :     {
    4212           0 :       GNUNET_break (0);
    4213           0 :       ctx->status = GNUNET_SYSERR;
    4214           0 :       return;
    4215             :     }
    4216           3 :     wire_method = json_string_value (t);
    4217           3 :     if (NULL == wire_method)
    4218             :     {
    4219           0 :       GNUNET_break (0);
    4220           0 :       ctx->status = GNUNET_SYSERR;
    4221           0 :       return;
    4222             :     }
    4223           3 :     ctx->cb (ctx->cb_cls,
    4224             :              rowid,
    4225             :              &merchant_pub,
    4226             :              wire_method,
    4227             :              &h_wire,
    4228             :              exec_time,
    4229             :              &h_contract_terms,
    4230             :              &coin_pub,
    4231             :              &amount_with_fee,
    4232             :              &deposit_fee);
    4233           3 :     GNUNET_PQ_cleanup_result (rs);
    4234             :   }
    4235             : }
    4236             : 
    4237             : 
    4238             : /**
    4239             :  * Lookup the list of Taler transactions that were aggregated
    4240             :  * into a wire transfer by the respective @a wtid.
    4241             :  *
    4242             :  * @param cls closure
    4243             :  * @param session database connection
    4244             :  * @param wtid the raw wire transfer identifier we used
    4245             :  * @param cb function to call on each transaction found
    4246             :  * @param cb_cls closure for @a cb
    4247             :  * @return query status of the transaction
    4248             :  */
    4249             : static enum GNUNET_DB_QueryStatus
    4250           5 : postgres_lookup_wire_transfer (void *cls,
    4251             :                                struct TALER_EXCHANGEDB_Session *session,
    4252             :                                const struct TALER_WireTransferIdentifierRawP *wtid,
    4253             :                                TALER_EXCHANGEDB_WireTransferDataCallback cb,
    4254             :                                void *cb_cls)
    4255             : {
    4256           5 :   struct GNUNET_PQ_QueryParam params[] = {
    4257             :     GNUNET_PQ_query_param_auto_from_type (wtid),
    4258             :     GNUNET_PQ_query_param_end
    4259             :   };
    4260             :   struct WireTransferResultContext ctx;
    4261             :   enum GNUNET_DB_QueryStatus qs;
    4262             :   
    4263           5 :   ctx.cb = cb;
    4264           5 :   ctx.cb_cls = cb_cls;
    4265           5 :   ctx.status = GNUNET_OK;
    4266             :   /* check if the melt record exists and get it */
    4267           5 :   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    4268             :                                              "lookup_transactions",
    4269             :                                              params,
    4270             :                                              &handle_wt_result,
    4271             :                                              &ctx);
    4272           5 :   if (GNUNET_OK != ctx.status)
    4273           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    4274           5 :   return qs;
    4275             : }
    4276             : 
    4277             : 
    4278             : /**
    4279             :  * Try to find the wire transfer details for a deposit operation.
    4280             :  * If we did not execute the deposit yet, return when it is supposed
    4281             :  * to be executed.
    4282             :  *
    4283             :  * @param cls closure
    4284             :  * @param session database connection
    4285             :  * @param h_contract_terms hash of the proposal data
    4286             :  * @param h_wire hash of merchant wire details
    4287             :  * @param coin_pub public key of deposited coin
    4288             :  * @param merchant_pub merchant public key
    4289             :  * @param cb function to call with the result
    4290             :  * @param cb_cls closure to pass to @a cb
    4291             :  * @return transaction status code
    4292             :  */
    4293             : static enum GNUNET_DB_QueryStatus
    4294           5 : postgres_wire_lookup_deposit_wtid (void *cls,
    4295             :                                    struct TALER_EXCHANGEDB_Session *session,
    4296             :                                    const struct GNUNET_HashCode *h_contract_terms,
    4297             :                                    const struct GNUNET_HashCode *h_wire,
    4298             :                                    const struct TALER_CoinSpendPublicKeyP *coin_pub,
    4299             :                                    const struct TALER_MerchantPublicKeyP *merchant_pub,
    4300             :                                    TALER_EXCHANGEDB_TrackTransactionCallback cb,
    4301             :                                    void *cb_cls)
    4302             : {
    4303             :   enum GNUNET_DB_QueryStatus qs;
    4304           5 :   struct GNUNET_PQ_QueryParam params[] = {
    4305             :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    4306             :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    4307             :     GNUNET_PQ_query_param_auto_from_type (h_wire),
    4308             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    4309             :     GNUNET_PQ_query_param_end
    4310             :   };
    4311             :   struct TALER_WireTransferIdentifierRawP wtid;
    4312             :   struct GNUNET_TIME_Absolute exec_time;
    4313             :   struct TALER_Amount amount_with_fee;
    4314             :   struct TALER_Amount deposit_fee;
    4315           5 :   struct GNUNET_PQ_ResultSpec rs[] = {
    4316             :     GNUNET_PQ_result_spec_auto_from_type ("wtid_raw", &wtid),
    4317             :     GNUNET_PQ_result_spec_absolute_time ("execution_date", &exec_time),
    4318             :     TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee),
    4319             :     TALER_PQ_result_spec_amount ("fee_deposit", &deposit_fee),
    4320             :     GNUNET_PQ_result_spec_end
    4321             :   };
    4322             :   
    4323             :   /* check if the melt record exists and get it */
    4324           5 :   qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    4325             :                                                  "lookup_deposit_wtid",
    4326             :                                                  params,
    4327             :                                                  rs);
    4328           5 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    4329             :   {
    4330           2 :     cb (cb_cls,
    4331             :         &wtid,
    4332             :         &amount_with_fee,
    4333             :         &deposit_fee,
    4334             :         exec_time);
    4335           2 :     return qs;
    4336             :   }
    4337           3 :   if (0 > qs)
    4338           0 :     return qs;
    4339             : 
    4340           3 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
    4341           3 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    4342             :               "lookup_deposit_wtid returned 0 matching rows\n");
    4343             :   {
    4344             :     /* Check if transaction exists in deposits, so that we just
    4345             :        do not have a WTID yet, if so, do call the CB with a NULL wtid
    4346             :        and return #GNUNET_YES! */
    4347           3 :     struct GNUNET_PQ_QueryParam params2[] = {
    4348             :       GNUNET_PQ_query_param_auto_from_type (coin_pub),
    4349             :       GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    4350             :       GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    4351             :       GNUNET_PQ_query_param_auto_from_type (h_wire),
    4352             :       GNUNET_PQ_query_param_end
    4353             :     };
    4354             :     struct GNUNET_TIME_Absolute exec_time;
    4355             :     struct TALER_Amount amount_with_fee;
    4356             :     struct TALER_Amount deposit_fee;
    4357           3 :     struct GNUNET_PQ_ResultSpec rs2[] = {
    4358             :       TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee),
    4359             :       TALER_PQ_result_spec_amount ("fee_deposit", &deposit_fee),
    4360             :       GNUNET_PQ_result_spec_absolute_time ("wire_deadline", &exec_time),
    4361             :       GNUNET_PQ_result_spec_end
    4362             :     };
    4363             : 
    4364           3 :     qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    4365             :                                                    "get_deposit_for_wtid",
    4366             :                                                    params2,
    4367             :                                                    rs2);
    4368           3 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    4369             :     {
    4370             :       /* Ok, we're aware of the transaction, but it has not yet been
    4371             :          executed */
    4372           1 :       cb (cb_cls,
    4373             :           NULL,
    4374             :           &amount_with_fee,
    4375             :           &deposit_fee,
    4376             :           exec_time);
    4377           1 :       return qs;
    4378             :     }
    4379           2 :     return qs;
    4380             :   }
    4381             : }
    4382             : 
    4383             : 
    4384             : /**
    4385             :  * Function called to insert aggregation information into the DB.
    4386             :  *
    4387             :  * @param cls closure
    4388             :  * @param session database connection
    4389             :  * @param wtid the raw wire transfer identifier we used
    4390             :  * @param deposit_serial_id row in the deposits table for which this is aggregation data
    4391             :  * @return transaction status code
    4392             :  */
    4393             : static enum GNUNET_DB_QueryStatus
    4394          39 : postgres_insert_aggregation_tracking (void *cls,
    4395             :                                       struct TALER_EXCHANGEDB_Session *session,
    4396             :                                       const struct TALER_WireTransferIdentifierRawP *wtid,
    4397             :                                       unsigned long long deposit_serial_id)
    4398             : {
    4399          39 :   uint64_t rid = deposit_serial_id;
    4400          39 :   struct GNUNET_PQ_QueryParam params[] = {
    4401             :     GNUNET_PQ_query_param_uint64 (&rid),
    4402             :     GNUNET_PQ_query_param_auto_from_type (wtid),
    4403             :     GNUNET_PQ_query_param_end
    4404             :   };
    4405             : 
    4406          39 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    4407             :                                              "insert_aggregation_tracking",
    4408             :                                              params);
    4409             : }
    4410             : 
    4411             : 
    4412             : /**
    4413             :  * Obtain wire fee from database.
    4414             :  *
    4415             :  * @param cls closure
    4416             :  * @param session database connection
    4417             :  * @param type type of wire transfer the fee applies for
    4418             :  * @param date for which date do we want the fee?
    4419             :  * @param[out] start_date when does the fee go into effect
    4420             :  * @param[out] end_date when does the fee end being valid
    4421             :  * @param[out] wire_fee how high is the wire transfer fee
    4422             :  * @param[out] master_sig signature over the above by the exchange master key
    4423             :  * @return status of the transaction
    4424             :  */
    4425             : static enum GNUNET_DB_QueryStatus
    4426          42 : postgres_get_wire_fee (void *cls,
    4427             :                        struct TALER_EXCHANGEDB_Session *session,
    4428             :                        const char *type,
    4429             :                        struct GNUNET_TIME_Absolute date,
    4430             :                        struct GNUNET_TIME_Absolute *start_date,
    4431             :                        struct GNUNET_TIME_Absolute *end_date,
    4432             :                        struct TALER_Amount *wire_fee,
    4433             :                        struct TALER_MasterSignatureP *master_sig)
    4434             : {
    4435          42 :   struct GNUNET_PQ_QueryParam params[] = {
    4436             :     GNUNET_PQ_query_param_string (type),
    4437             :     GNUNET_PQ_query_param_absolute_time (&date),
    4438             :     GNUNET_PQ_query_param_end
    4439             :   };
    4440          42 :   struct GNUNET_PQ_ResultSpec rs[] = {
    4441             :     GNUNET_PQ_result_spec_absolute_time ("start_date", start_date),
    4442             :     GNUNET_PQ_result_spec_absolute_time ("end_date", end_date),
    4443             :     TALER_PQ_result_spec_amount ("wire_fee", wire_fee),
    4444             :     GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig),
    4445             :     GNUNET_PQ_result_spec_end
    4446             :   };
    4447             : 
    4448          42 :   return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    4449             :                                                    "get_wire_fee",
    4450             :                                                    params,
    4451             :                                                    rs);
    4452             : }
    4453             : 
    4454             : 
    4455             : /**
    4456             :  * Insert wire transfer fee into database.
    4457             :  *
    4458             :  * @param cls closure
    4459             :  * @param session database connection
    4460             :  * @param type type of wire transfer this fee applies for
    4461             :  * @param start_date when does the fee go into effect
    4462             :  * @param end_date when does the fee end being valid
    4463             :  * @param wire_fee how high is the wire transfer fee
    4464             :  * @param master_sig signature over the above by the exchange master key
    4465             :  * @return transaction status code
    4466             :  */
    4467             : static enum GNUNET_DB_QueryStatus
    4468          38 : postgres_insert_wire_fee (void *cls,
    4469             :                           struct TALER_EXCHANGEDB_Session *session,
    4470             :                           const char *type,
    4471             :                           struct GNUNET_TIME_Absolute start_date,
    4472             :                           struct GNUNET_TIME_Absolute end_date,
    4473             :                           const struct TALER_Amount *wire_fee,
    4474             :                           const struct TALER_MasterSignatureP *master_sig)
    4475             : {
    4476          38 :   struct GNUNET_PQ_QueryParam params[] = {
    4477             :     GNUNET_PQ_query_param_string (type),
    4478             :     GNUNET_PQ_query_param_absolute_time (&start_date),
    4479             :     GNUNET_PQ_query_param_absolute_time (&end_date),
    4480             :     TALER_PQ_query_param_amount (wire_fee),
    4481             :     GNUNET_PQ_query_param_auto_from_type (master_sig),
    4482             :     GNUNET_PQ_query_param_end
    4483             :   };
    4484             :   struct TALER_Amount wf;
    4485             :   struct TALER_MasterSignatureP sig;
    4486             :   struct GNUNET_TIME_Absolute sd;
    4487             :   struct GNUNET_TIME_Absolute ed;
    4488             :   enum GNUNET_DB_QueryStatus qs;
    4489             : 
    4490          38 :   qs = postgres_get_wire_fee (cls,
    4491             :                               session,
    4492             :                               type,
    4493             :                               start_date,
    4494             :                               &sd,
    4495             :                               &ed,
    4496             :                               &wf,
    4497             :                               &sig);
    4498          38 :   if (qs < 0)
    4499           0 :     return qs;
    4500          38 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    4501             :   {
    4502          30 :     if (0 != memcmp (&sig,
    4503             :                      master_sig,
    4504             :                      sizeof (sig)))
    4505             :     {
    4506           0 :       GNUNET_break (0);
    4507           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    4508             :     }
    4509          30 :     if (0 != TALER_amount_cmp (wire_fee,
    4510             :                                &wf))
    4511             :     {
    4512           0 :       GNUNET_break (0);
    4513           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    4514             :     }
    4515          60 :     if ( (sd.abs_value_us != start_date.abs_value_us) ||
    4516          30 :          (ed.abs_value_us != end_date.abs_value_us) )
    4517             :     {
    4518           0 :       GNUNET_break (0);
    4519           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    4520             :     }
    4521             :     /* equal record already exists */
    4522          30 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    4523             :   }
    4524             : 
    4525           8 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    4526             :                                              "insert_wire_fee",
    4527             :                                              params);
    4528             : }
    4529             : 
    4530             : 
    4531             : /**
    4532             :  * Closure for #reserve_expired_cb().
    4533             :  */
    4534             : struct ExpiredReserveContext
    4535             : {
    4536             :   /**
    4537             :    * Function to call for each expired reserve.
    4538             :    */
    4539             :   TALER_EXCHANGEDB_ReserveExpiredCallback rec;
    4540             : 
    4541             :   /**
    4542             :    * Closure to give to @e rec.
    4543             :    */
    4544             :   void *rec_cls;
    4545             : 
    4546             :   /**
    4547             :    * Set to #GNUNET_SYSERR on error.
    4548             :    */
    4549             :   int status;
    4550             : };
    4551             : 
    4552             : 
    4553             : /**
    4554             :  * Function to be called with the results of a SELECT statement
    4555             :  * that has returned @a num_results results.
    4556             :  *
    4557             :  * @param cls closure
    4558             :  * @param result the postgres result
    4559             :  * @param num_result the number of results in @a result
    4560             :  */
    4561             : static void
    4562          45 : reserve_expired_cb (void *cls,
    4563             :                     PGresult *result,
    4564             :                     unsigned int num_results)
    4565             : {
    4566          45 :   struct ExpiredReserveContext *erc = cls;
    4567             :   int ret;
    4568             : 
    4569          45 :   ret = GNUNET_OK;
    4570          92 :   for (unsigned int i=0;i<num_results;i++)
    4571             :   {
    4572             :     struct GNUNET_TIME_Absolute exp_date;
    4573             :     json_t *account_details;
    4574             :     struct TALER_ReservePublicKeyP reserve_pub;
    4575             :     struct TALER_Amount remaining_balance;
    4576           1 :     struct GNUNET_PQ_ResultSpec rs[] = {
    4577             :       GNUNET_PQ_result_spec_absolute_time ("expiration_date",
    4578             :                                            &exp_date),
    4579             :       TALER_PQ_result_spec_json ("account_details",
    4580             :                                  &account_details),
    4581             :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    4582             :                                             &reserve_pub),
    4583             :       TALER_PQ_result_spec_amount ("current_balance",
    4584             :                                    &remaining_balance),
    4585             :       GNUNET_PQ_result_spec_end
    4586             :     };
    4587             : 
    4588           1 :     if (GNUNET_OK !=
    4589           1 :         GNUNET_PQ_extract_result (result,
    4590             :                                   rs,
    4591             :                                   i))
    4592             :     {
    4593           0 :       GNUNET_break (0);
    4594           0 :       ret = GNUNET_SYSERR;
    4595           0 :       break;
    4596             :     }
    4597           1 :     ret = erc->rec (erc->rec_cls,
    4598             :                     &reserve_pub,
    4599             :                     &remaining_balance,
    4600             :                     account_details,
    4601             :                     exp_date);
    4602           1 :     GNUNET_PQ_cleanup_result (rs);
    4603           1 :     if (GNUNET_OK != ret)
    4604           0 :       break;
    4605             :   }
    4606          45 :   erc->status = ret;
    4607          45 : }
    4608             : 
    4609             : 
    4610             : /**
    4611             :  * Obtain information about expired reserves and their
    4612             :  * remaining balances.
    4613             :  *
    4614             :  * @param cls closure of the plugin
    4615             :  * @param session database connection
    4616             :  * @param now timestamp based on which we decide expiration
    4617             :  * @param rec function to call on expired reserves
    4618             :  * @param rec_cls closure for @a rec
    4619             :  * @return transaction status
    4620             :  */
    4621             : static enum GNUNET_DB_QueryStatus
    4622          45 : postgres_get_expired_reserves (void *cls,
    4623             :                                struct TALER_EXCHANGEDB_Session *session,
    4624             :                                struct GNUNET_TIME_Absolute now,
    4625             :                                TALER_EXCHANGEDB_ReserveExpiredCallback rec,
    4626             :                                void *rec_cls)
    4627             : {
    4628          45 :   struct GNUNET_PQ_QueryParam params[] = {
    4629             :     GNUNET_PQ_query_param_absolute_time (&now),
    4630             :     GNUNET_PQ_query_param_end
    4631             :   };
    4632             :   struct ExpiredReserveContext ectx;
    4633             :   enum GNUNET_DB_QueryStatus qs;
    4634             :   
    4635          45 :   ectx.rec = rec;
    4636          45 :   ectx.rec_cls = rec_cls;
    4637          45 :   ectx.status = GNUNET_OK;
    4638          45 :   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    4639             :                                              "get_expired_reserves",
    4640             :                                              params,
    4641             :                                              &reserve_expired_cb,
    4642             :                                              &ectx);
    4643          45 :   if (GNUNET_OK != ectx.status)
    4644           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    4645          45 :   return qs;
    4646             : }
    4647             : 
    4648             : 
    4649             : /**
    4650             :  * Insert reserve close operation into database.
    4651             :  *
    4652             :  * @param cls closure
    4653             :  * @param session database connection
    4654             :  * @param reserve_pub which reserve is this about?
    4655             :  * @param execution_date when did we perform the transfer?
    4656             :  * @param receiver_account to which account do we transfer?
    4657             :  * @param wtid wire transfer details
    4658             :  * @param amount_with_fee amount we charged to the reserve
    4659             :  * @param closing_fee how high is the closing fee
    4660             :  * @return transaction status code
    4661             :  */
    4662             : static enum GNUNET_DB_QueryStatus
    4663           2 : postgres_insert_reserve_closed (void *cls,
    4664             :                                 struct TALER_EXCHANGEDB_Session *session,
    4665             :                                 const struct TALER_ReservePublicKeyP *reserve_pub,
    4666             :                                 struct GNUNET_TIME_Absolute execution_date,
    4667             :                                 const json_t *receiver_account,
    4668             :                                 const struct TALER_WireTransferIdentifierRawP *wtid,
    4669             :                                 const struct TALER_Amount *amount_with_fee,
    4670             :                                 const struct TALER_Amount *closing_fee)
    4671             : {
    4672             :   struct TALER_EXCHANGEDB_Reserve reserve;
    4673           2 :   struct GNUNET_PQ_QueryParam params[] = {
    4674             :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    4675             :     GNUNET_PQ_query_param_absolute_time (&execution_date),
    4676             :     GNUNET_PQ_query_param_auto_from_type (wtid),
    4677             :     TALER_PQ_query_param_json (receiver_account),
    4678             :     TALER_PQ_query_param_amount (amount_with_fee),
    4679             :     TALER_PQ_query_param_amount (closing_fee),
    4680             :     GNUNET_PQ_query_param_end
    4681             :   };
    4682             :   int ret;
    4683             :   enum GNUNET_DB_QueryStatus qs;
    4684             : 
    4685           2 :   qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
    4686             :                                            "reserves_close_insert",
    4687             :                                            params);
    4688           2 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    4689           0 :     return qs;
    4690             : 
    4691             :   /* update reserve balance */
    4692           2 :   reserve.pub = *reserve_pub;
    4693           2 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    4694             :       (qs = postgres_reserve_get (cls,
    4695             :                                   session,
    4696             :                                   &reserve)))
    4697             :   {
    4698             :     /* Existence should have been checked before we got here... */
    4699           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    4700           0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    4701           0 :       qs = GNUNET_DB_STATUS_HARD_ERROR;
    4702           0 :     return qs;
    4703             :   }
    4704           2 :   ret = TALER_amount_subtract (&reserve.balance,
    4705             :                                &reserve.balance,
    4706             :                                amount_with_fee);
    4707           2 :   if (GNUNET_SYSERR == ret)
    4708             :   {
    4709             :     /* The reserve history was checked to make sure there is enough of a balance
    4710             :        left before we tried this; however, concurrent operations may have changed
    4711             :        the situation by now.  We should re-try the transaction.  */
    4712           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4713             :                 "Closing of reserve `%s' refused due to balance missmatch. Retrying.\n",
    4714             :                 TALER_B2S (reserve_pub));
    4715           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    4716             :   }
    4717           2 :   GNUNET_break (GNUNET_NO == ret);
    4718           2 :   return reserves_update (cls,
    4719             :                           session,
    4720             :                           &reserve);
    4721             : }
    4722             : 
    4723             : 
    4724             : /**
    4725             :  * Function called to insert wire transfer commit data into the DB.
    4726             :  *
    4727             :  * @param cls closure
    4728             :  * @param session database connection
    4729             :  * @param type type of the wire transfer (i.e. "sepa")
    4730             :  * @param buf buffer with wire transfer preparation data
    4731             :  * @param buf_size number of bytes in @a buf
    4732             :  * @return query status code
    4733             :  */
    4734             : static enum GNUNET_DB_QueryStatus
    4735          19 : postgres_wire_prepare_data_insert (void *cls,
    4736             :                                    struct TALER_EXCHANGEDB_Session *session,
    4737             :                                    const char *type,
    4738             :                                    const char *buf,
    4739             :                                    size_t buf_size)
    4740             : {
    4741          19 :   struct GNUNET_PQ_QueryParam params[] = {
    4742             :     GNUNET_PQ_query_param_string (type),
    4743             :     GNUNET_PQ_query_param_fixed_size (buf, buf_size),
    4744             :     GNUNET_PQ_query_param_end
    4745             :   };
    4746             : 
    4747          19 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    4748             :                                              "wire_prepare_data_insert",
    4749             :                                              params);
    4750             : }
    4751             : 
    4752             : 
    4753             : /**
    4754             :  * Function called to mark wire transfer commit data as finished.
    4755             :  *
    4756             :  * @param cls closure
    4757             :  * @param session database connection
    4758             :  * @param rowid which entry to mark as finished
    4759             :  * @return transaction status code
    4760             :  */
    4761             : static enum GNUNET_DB_QueryStatus
    4762          19 : postgres_wire_prepare_data_mark_finished (void *cls,
    4763             :                                           struct TALER_EXCHANGEDB_Session *session,
    4764             :                                           uint64_t rowid)
    4765             : {
    4766          19 :   struct GNUNET_PQ_QueryParam params[] = {
    4767             :     GNUNET_PQ_query_param_uint64 (&rowid),
    4768             :     GNUNET_PQ_query_param_end
    4769             :   };
    4770             : 
    4771          19 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    4772             :                                              "wire_prepare_data_mark_done",
    4773             :                                              params);
    4774             : }
    4775             : 
    4776             : 
    4777             : /**
    4778             :  * Function called to get an unfinished wire transfer
    4779             :  * preparation data. Fetches at most one item.
    4780             :  *
    4781             :  * @param cls closure
    4782             :  * @param session database connection
    4783             :  * @param cb function to call for ONE unfinished item
    4784             :  * @param cb_cls closure for @a cb
    4785             :  * @return transaction status code
    4786             :  */
    4787             : static enum GNUNET_DB_QueryStatus
    4788          67 : postgres_wire_prepare_data_get (void *cls,
    4789             :                                 struct TALER_EXCHANGEDB_Session *session,
    4790             :                                 TALER_EXCHANGEDB_WirePreparationIterator cb,
    4791             :                                 void *cb_cls)
    4792             : {
    4793             :   enum GNUNET_DB_QueryStatus qs;
    4794          67 :   struct GNUNET_PQ_QueryParam params[] = {
    4795             :     GNUNET_PQ_query_param_end
    4796             :   };
    4797             :   uint64_t prewire_uuid;
    4798             :   char *type;
    4799          67 :   void *buf = NULL;
    4800             :   size_t buf_size;
    4801          67 :   struct GNUNET_PQ_ResultSpec rs[] = {
    4802             :     GNUNET_PQ_result_spec_uint64 ("prewire_uuid",
    4803             :                                   &prewire_uuid),
    4804             :     GNUNET_PQ_result_spec_string ("type",
    4805             :                                   &type),
    4806             :     GNUNET_PQ_result_spec_variable_size ("buf",
    4807             :                                          &buf,
    4808             :                                          &buf_size),
    4809             :     GNUNET_PQ_result_spec_end
    4810             :   };
    4811             : 
    4812          67 :   qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    4813             :                                                  "wire_prepare_data_get",
    4814             :                                                  params,
    4815             :                                                  rs);
    4816          67 :   if (0 >= qs)
    4817          48 :     return qs;
    4818          19 :   cb (cb_cls,
    4819             :       prewire_uuid,
    4820             :       type,
    4821             :       buf,
    4822             :       buf_size);
    4823          19 :   GNUNET_PQ_cleanup_result (rs);
    4824          19 :   return qs;
    4825             : }
    4826             : 
    4827             : 
    4828             : /**
    4829             :  * Start a transaction where we transiently violate the foreign
    4830             :  * constraints on the "wire_out" table as we insert aggregations
    4831             :  * and only add the wire transfer out at the end.
    4832             :  *
    4833             :  * @param cls the @e cls of this struct with the plugin-specific state
    4834             :  * @param session connection to use
    4835             :  * @return #GNUNET_OK on success
    4836             :  */
    4837             : static int
    4838          62 : postgres_start_deferred_wire_out (void *cls,
    4839             :                                   struct TALER_EXCHANGEDB_Session *session)
    4840             : {
    4841             :   PGresult *result;
    4842             :   ExecStatusType ex;
    4843             : 
    4844          62 :   result = PQexec (session->conn,
    4845             :                    "START TRANSACTION ISOLATION LEVEL SERIALIZABLE");
    4846          62 :   if (PGRES_COMMAND_OK !=
    4847             :       (ex = PQresultStatus (result)))
    4848             :   {
    4849           0 :     TALER_LOG_ERROR ("Failed to start transaction (%s): %s\n",
    4850             :                      PQresStatus (ex),
    4851             :                      PQerrorMessage (session->conn));
    4852           0 :     GNUNET_break (0);
    4853           0 :     PQclear (result);
    4854           0 :     return GNUNET_SYSERR;
    4855             :   }
    4856          62 :   PQclear (result);
    4857          62 :   result = PQexec (session->conn,
    4858             :                    "SET CONSTRAINTS wire_out_ref DEFERRED");
    4859          62 :   if (PGRES_COMMAND_OK !=
    4860             :       (ex = PQresultStatus (result)))
    4861             :   {
    4862           0 :     TALER_LOG_ERROR ("Failed to defer wire_out_ref constraint on transaction (%s): %s\n",
    4863             :                      PQresStatus (ex),
    4864             :                      PQerrorMessage (session->conn));
    4865           0 :     GNUNET_break (0);
    4866           0 :     PQclear (result);
    4867           0 :     return GNUNET_SYSERR;
    4868             :   }
    4869          62 :   PQclear (result);
    4870          62 :   return GNUNET_OK;
    4871             : }
    4872             : 
    4873             : 
    4874             : /**
    4875             :  * Store information about an outgoing wire transfer that was executed.
    4876             :  *
    4877             :  * @param cls closure
    4878             :  * @param session database connection
    4879             :  * @param date time of the wire transfer
    4880             :  * @param wtid subject of the wire transfer
    4881             :  * @param wire_account details about the receiver account of the wire transfer
    4882             :  * @param amount amount that was transmitted
    4883             :  * @return transaction status code
    4884             :  */
    4885             : static enum GNUNET_DB_QueryStatus
    4886          18 : postgres_store_wire_transfer_out (void *cls,
    4887             :                                   struct TALER_EXCHANGEDB_Session *session,
    4888             :                                   struct GNUNET_TIME_Absolute date,
    4889             :                                   const struct TALER_WireTransferIdentifierRawP *wtid,
    4890             :                                   const json_t *wire_account,
    4891             :                                   const struct TALER_Amount *amount)
    4892             : {
    4893          18 :   struct GNUNET_PQ_QueryParam params[] = {
    4894             :     GNUNET_PQ_query_param_absolute_time (&date),
    4895             :     GNUNET_PQ_query_param_auto_from_type (wtid),
    4896             :     TALER_PQ_query_param_json (wire_account),
    4897             :     TALER_PQ_query_param_amount (amount),
    4898             :     GNUNET_PQ_query_param_end
    4899             :   };
    4900             : 
    4901          18 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    4902             :                                              "insert_wire_out",
    4903             :                                              params);
    4904             : }
    4905             : 
    4906             : 
    4907             : /**
    4908             :  * Function called to perform "garbage collection" on the
    4909             :  * database, expiring records we no longer require.
    4910             :  *
    4911             :  * @param cls closure
    4912             :  * @return #GNUNET_OK on success,
    4913             :  *         #GNUNET_SYSERR on DB errors
    4914             :  */
    4915             : static int
    4916           1 : postgres_gc (void *cls)
    4917             : {
    4918           1 :   struct PostgresClosure *pc = cls;
    4919             :   struct GNUNET_TIME_Absolute now;
    4920             :   struct GNUNET_TIME_Absolute long_ago;
    4921           1 :   struct GNUNET_PQ_QueryParam params_none[] = {
    4922             :     GNUNET_PQ_query_param_end
    4923             :   };
    4924           1 :   struct GNUNET_PQ_QueryParam params_time[] = {
    4925             :     GNUNET_PQ_query_param_absolute_time (&now),
    4926             :     GNUNET_PQ_query_param_end
    4927             :   };
    4928           1 :   struct GNUNET_PQ_QueryParam params_ancient_time[] = {
    4929             :     GNUNET_PQ_query_param_absolute_time (&long_ago),
    4930             :     GNUNET_PQ_query_param_end
    4931             :   };
    4932             :   PGconn *conn;
    4933             :   int ret;
    4934             : 
    4935           1 :   now = GNUNET_TIME_absolute_get ();
    4936             :   /* Keep wire fees for 10 years, that should always
    4937             :      be enough _and_ they are tiny so it does not
    4938             :      matter to make this tight */
    4939           1 :   long_ago = GNUNET_TIME_absolute_subtract (now,
    4940             :                                             GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
    4941             :                                                                            10));
    4942           1 :   conn = GNUNET_PQ_connect (pc->connection_cfg_str);
    4943           1 :   if (NULL == conn)
    4944           0 :     return GNUNET_SYSERR;
    4945           1 :   ret = postgres_prepare (conn);
    4946           1 :   if (GNUNET_OK == ret)
    4947             :   {
    4948           1 :     if (
    4949           1 :          (0 > GNUNET_PQ_eval_prepared_non_select (conn,
    4950             :                                                   "gc_reserves",
    4951           1 :                                                   params_time)) ||
    4952           1 :          (0 > GNUNET_PQ_eval_prepared_non_select (conn,
    4953             :                                                   "gc_prewire",
    4954           1 :                                                   params_none)) ||
    4955           1 :          (0 > GNUNET_PQ_eval_prepared_non_select (conn,
    4956             :                                                   "gc_wire_fee",
    4957             :                                                   params_ancient_time))
    4958             :         )
    4959           0 :       ret = GNUNET_SYSERR;
    4960             :     /* This one may fail due to foreign key constraints from
    4961             :        payback and reserves_out tables to known_coins; these
    4962             :        are NOT using 'ON DROP CASCADE' and might keep denomination
    4963             :        keys alive for a bit longer, thus causing this statement
    4964             :        to fail. */
    4965           1 :     (void) GNUNET_PQ_eval_prepared_non_select (conn,
    4966             :                                                "gc_denominations",
    4967             :                                                params_time);    
    4968             :   }
    4969           1 :   PQfinish (conn);
    4970           1 :   return ret;
    4971             : }
    4972             : 
    4973             : 
    4974             : /**
    4975             :  * Closure for #deposit_serial_helper_cb().
    4976             :  */
    4977             : struct DepositSerialContext
    4978             : {
    4979             : 
    4980             :   /**
    4981             :    * Callback to call.
    4982             :    */
    4983             :   TALER_EXCHANGEDB_DepositCallback cb;
    4984             :   
    4985             :   /**
    4986             :    * Closure for @e cb.
    4987             :    */
    4988             :   void *cb_cls;
    4989             :   
    4990             :   /**
    4991             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    4992             :    */ 
    4993             :   int status;
    4994             : };
    4995             : 
    4996             : 
    4997             : /**
    4998             :  * Helper function to be called with the results of a SELECT statement
    4999             :  * that has returned @a num_results results.
    5000             :  *
    5001             :  * @param cls closure of type `struct DepositSerialContext`
    5002             :  * @param result the postgres result
    5003             :  * @param num_result the number of results in @a result
    5004             :  */
    5005             : static void
    5006           1 : deposit_serial_helper_cb (void *cls,
    5007             :                           PGresult *result,
    5008             :                           unsigned int num_results)
    5009             : {
    5010           1 :   struct DepositSerialContext *dsc = cls;
    5011             :   
    5012           2 :   for (unsigned int i=0;i<num_results;i++)
    5013             :   {
    5014             :     struct TALER_EXCHANGEDB_Deposit deposit;
    5015             :     struct TALER_DenominationPublicKey denom_pub;
    5016           1 :     uint8_t done = 0;
    5017             :     uint64_t rowid;
    5018           1 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5019             :       TALER_PQ_result_spec_amount ("amount_with_fee",
    5020             :                                    &deposit.amount_with_fee),
    5021             :       GNUNET_PQ_result_spec_absolute_time ("timestamp",
    5022             :                                           &deposit.timestamp),
    5023             :       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    5024             :                                             &deposit.merchant_pub),
    5025             :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    5026             :                                             &denom_pub.rsa_public_key),
    5027             :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    5028             :                                            &deposit.coin.coin_pub),
    5029             :       GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    5030             :                                            &deposit.csig),
    5031             :       GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
    5032             :                                            &deposit.refund_deadline),
    5033             :       GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
    5034             :                                            &deposit.wire_deadline),
    5035             :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    5036             :                                            &deposit.h_contract_terms),
    5037             :       TALER_PQ_result_spec_json ("wire",
    5038             :                                  &deposit.receiver_wire_account),
    5039             :       GNUNET_PQ_result_spec_auto_from_type ("done",
    5040             :                                             &done),
    5041             :       GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
    5042             :                                     &rowid),
    5043             :       GNUNET_PQ_result_spec_end
    5044             :     };
    5045             :     int ret;
    5046             :     
    5047           1 :     if (GNUNET_OK !=
    5048           1 :         GNUNET_PQ_extract_result (result,
    5049             :                                   rs,
    5050             :                                   i))
    5051             :     {
    5052           0 :       GNUNET_break (0);
    5053           0 :       dsc->status = GNUNET_SYSERR;
    5054           0 :       return;
    5055             :     }
    5056           2 :     ret = dsc->cb (dsc->cb_cls,
    5057             :                    rowid,
    5058             :                    deposit.timestamp,
    5059             :                    &deposit.merchant_pub,
    5060             :                    &denom_pub,
    5061             :                    &deposit.coin.coin_pub,
    5062             :                    &deposit.csig,
    5063             :                    &deposit.amount_with_fee,
    5064             :                    &deposit.h_contract_terms,
    5065             :                    deposit.refund_deadline,
    5066             :                    deposit.wire_deadline,
    5067           1 :                    deposit.receiver_wire_account,
    5068             :                    done);
    5069           1 :     GNUNET_PQ_cleanup_result (rs);
    5070           1 :     if (GNUNET_OK != ret)
    5071           0 :       break;
    5072             :   }
    5073             : }
    5074             : 
    5075             : 
    5076             : /**
    5077             :  * Select deposits above @a serial_id in monotonically increasing
    5078             :  * order.
    5079             :  *
    5080             :  * @param cls closure
    5081             :  * @param session database connection
    5082             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    5083             :  * @param cb function to call on each result
    5084             :  * @param cb_cls closure for @a cb
    5085             :  * @return transaction status code
    5086             :  */
    5087             : static enum GNUNET_DB_QueryStatus
    5088           1 : postgres_select_deposits_above_serial_id (void *cls,
    5089             :                                           struct TALER_EXCHANGEDB_Session *session,
    5090             :                                           uint64_t serial_id,
    5091             :                                           TALER_EXCHANGEDB_DepositCallback cb,
    5092             :                                           void *cb_cls)
    5093             : {
    5094           1 :   struct GNUNET_PQ_QueryParam params[] = {
    5095             :     GNUNET_PQ_query_param_uint64 (&serial_id),
    5096             :     GNUNET_PQ_query_param_end
    5097             :   };
    5098           1 :   struct DepositSerialContext dsc = {
    5099             :     .cb = cb,
    5100             :     .cb_cls = cb_cls,
    5101             :     .status = GNUNET_OK
    5102             :   };
    5103             :   enum GNUNET_DB_QueryStatus qs;
    5104             : 
    5105           1 :   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    5106             :                                              "audit_get_deposits_incr",
    5107             :                                              params,
    5108             :                                              &deposit_serial_helper_cb,
    5109             :                                              &dsc);
    5110           1 :   if (GNUNET_OK != dsc.status)
    5111           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    5112           1 :   return qs;
    5113             : }
    5114             : 
    5115             : 
    5116             : /**
    5117             :  * Closure for #refreshs_serial_helper_cb().
    5118             :  */
    5119             : struct RefreshsSerialContext
    5120             : {
    5121             : 
    5122             :   /**
    5123             :    * Callback to call.
    5124             :    */
    5125             :   TALER_EXCHANGEDB_RefreshSessionCallback cb;
    5126             :   
    5127             :   /**
    5128             :    * Closure for @e cb.
    5129             :    */
    5130             :   void *cb_cls;
    5131             :   
    5132             :   /**
    5133             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    5134             :    */ 
    5135             :   int status;
    5136             : };
    5137             : 
    5138             : 
    5139             : /**
    5140             :  * Helper function to be called with the results of a SELECT statement
    5141             :  * that has returned @a num_results results.
    5142             :  *
    5143             :  * @param cls closure of type `struct RefreshsSerialContext`
    5144             :  * @param result the postgres result
    5145             :  * @param num_result the number of results in @a result
    5146             :  */
    5147             : static void
    5148           1 : refreshs_serial_helper_cb (void *cls,
    5149             :                            PGresult *result,
    5150             :                            unsigned int num_results)
    5151             : {
    5152           1 :   struct RefreshsSerialContext *rsc = cls;
    5153             :   
    5154           2 :   for (unsigned int i=0;i<num_results;i++)
    5155             :   {
    5156             :     struct TALER_DenominationPublicKey denom_pub;
    5157             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    5158             :     struct TALER_CoinSpendSignatureP coin_sig;
    5159             :     struct TALER_Amount amount_with_fee;
    5160             :     uint16_t num_newcoins;
    5161             :     uint16_t noreveal_index;
    5162             :     uint64_t rowid;
    5163             :     struct GNUNET_HashCode session_hash;
    5164           1 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5165             :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    5166             :                                             &denom_pub.rsa_public_key),
    5167             :       GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
    5168             :                                             &coin_pub),
    5169             :       GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
    5170             :                                             &coin_sig),
    5171             :       TALER_PQ_result_spec_amount ("amount_with_fee",
    5172             :                                    &amount_with_fee),
    5173             :       GNUNET_PQ_result_spec_uint16 ("num_newcoins",
    5174             :                                     &num_newcoins),
    5175             :       GNUNET_PQ_result_spec_uint16 ("noreveal_index",
    5176             :                                     &noreveal_index),
    5177             :       GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
    5178             :                                     &rowid),
    5179             :       GNUNET_PQ_result_spec_auto_from_type ("session_hash",
    5180             :                                             &session_hash),
    5181             :       GNUNET_PQ_result_spec_end
    5182             :     };
    5183             :     int ret;
    5184             : 
    5185           1 :     if (GNUNET_OK !=
    5186           1 :         GNUNET_PQ_extract_result (result,
    5187             :                                   rs,
    5188             :                                   i))
    5189             :     {
    5190           0 :       GNUNET_break (0);
    5191           0 :       rsc->status = GNUNET_SYSERR;
    5192           0 :       return;
    5193             :     }
    5194           1 :     ret = rsc->cb (rsc->cb_cls,
    5195             :                    rowid,
    5196             :                    &denom_pub,
    5197             :                    &coin_pub,
    5198             :                    &coin_sig,
    5199             :                    &amount_with_fee,
    5200             :                    num_newcoins,
    5201             :                    noreveal_index,
    5202             :                    &session_hash);
    5203           1 :     GNUNET_PQ_cleanup_result (rs);
    5204           1 :     if (GNUNET_OK != ret)
    5205           0 :       break;
    5206             :   }
    5207             : }
    5208             : 
    5209             : 
    5210             : /**
    5211             :  * Select refresh sessions above @a serial_id in monotonically increasing
    5212             :  * order.
    5213             :  *
    5214             :  * @param cls closure
    5215             :  * @param session database connection
    5216             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    5217             :  * @param cb function to call on each result
    5218             :  * @param cb_cls closure for @a cb
    5219             :  * @return transaction status code
    5220             :  */
    5221             : static enum GNUNET_DB_QueryStatus
    5222           1 : postgres_select_refreshs_above_serial_id (void *cls,
    5223             :                                           struct TALER_EXCHANGEDB_Session *session,
    5224             :                                           uint64_t serial_id,
    5225             :                                           TALER_EXCHANGEDB_RefreshSessionCallback cb,
    5226             :                                           void *cb_cls)
    5227             : {
    5228           1 :   struct GNUNET_PQ_QueryParam params[] = {
    5229             :     GNUNET_PQ_query_param_uint64 (&serial_id),
    5230             :     GNUNET_PQ_query_param_end
    5231             :   };
    5232           1 :   struct RefreshsSerialContext rsc = {
    5233             :     .cb = cb,
    5234             :     .cb_cls = cb_cls,
    5235             :     .status = GNUNET_OK
    5236             :   };
    5237             :   enum GNUNET_DB_QueryStatus qs;
    5238             : 
    5239           1 :   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    5240             :                                              "audit_get_refresh_sessions_incr",
    5241             :                                              params,
    5242             :                                              &refreshs_serial_helper_cb,
    5243             :                                              &rsc);
    5244           1 :   if (GNUNET_OK != rsc.status)
    5245           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    5246           1 :   return qs;
    5247             : }
    5248             : 
    5249             : 
    5250             : /**
    5251             :  * Closure for #refunds_serial_helper_cb().
    5252             :  */
    5253             : struct RefundsSerialContext
    5254             : {
    5255             : 
    5256             :   /**
    5257             :    * Callback to call.
    5258             :    */
    5259             :   TALER_EXCHANGEDB_RefundCallback cb;
    5260             :   
    5261             :   /**
    5262             :    * Closure for @e cb.
    5263             :    */
    5264             :   void *cb_cls;
    5265             :   
    5266             :   /**
    5267             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    5268             :    */ 
    5269             :   int status;
    5270             : };
    5271             : 
    5272             : 
    5273             : /**
    5274             :  * Helper function to be called with the results of a SELECT statement
    5275             :  * that has returned @a num_results results.
    5276             :  *
    5277             :  * @param cls closure of type `struct RefundsSerialContext`
    5278             :  * @param result the postgres result
    5279             :  * @param num_result the number of results in @a result
    5280             :  */
    5281             : static void
    5282           1 : refunds_serial_helper_cb (void *cls,
    5283             :                           PGresult *result,
    5284             :                           unsigned int num_results)
    5285             : {
    5286           1 :   struct RefundsSerialContext *rsc = cls;
    5287             :   
    5288           2 :   for (unsigned int i=0;i<num_results;i++)
    5289             :   {
    5290             :     struct TALER_EXCHANGEDB_Refund refund;
    5291             :     struct TALER_DenominationPublicKey denom_pub;
    5292             :     uint64_t rowid;
    5293           1 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5294             :       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
    5295             :                                             &refund.merchant_pub),
    5296             :       GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
    5297             :                                            &refund.merchant_sig),
    5298             :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    5299             :                                            &refund.h_contract_terms),
    5300             :       GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
    5301             :                                     &refund.rtransaction_id),
    5302             :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    5303             :                                             &denom_pub.rsa_public_key),
    5304             :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    5305             :                                            &refund.coin.coin_pub),
    5306             :       TALER_PQ_result_spec_amount ("amount_with_fee",
    5307             :                                    &refund.refund_amount),
    5308             :       GNUNET_PQ_result_spec_uint64 ("refund_serial_id",
    5309             :                                     &rowid),
    5310             :       GNUNET_PQ_result_spec_end
    5311             :     };
    5312             :     int ret;
    5313             :     
    5314           1 :     if (GNUNET_OK !=
    5315           1 :         GNUNET_PQ_extract_result (result,
    5316             :                                   rs,
    5317             :                                   i))
    5318             :     {
    5319           0 :       GNUNET_break (0);
    5320           0 :       rsc->status = GNUNET_SYSERR;
    5321           0 :       return;
    5322             :     }
    5323           1 :     ret = rsc->cb (rsc->cb_cls,
    5324             :                    rowid,
    5325             :                    &denom_pub,
    5326             :                    &refund.coin.coin_pub,
    5327             :                    &refund.merchant_pub,
    5328             :                    &refund.merchant_sig,
    5329             :                    &refund.h_contract_terms,
    5330             :                    refund.rtransaction_id,
    5331             :                    &refund.refund_amount);
    5332           1 :     GNUNET_PQ_cleanup_result (rs);
    5333           1 :     if (GNUNET_OK != ret)
    5334           0 :       break;
    5335             :   }
    5336             : }
    5337             : 
    5338             : 
    5339             : /**
    5340             :  * Select refunds above @a serial_id in monotonically increasing
    5341             :  * order.
    5342             :  *
    5343             :  * @param cls closure
    5344             :  * @param session database connection
    5345             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    5346             :  * @param cb function to call on each result
    5347             :  * @param cb_cls closure for @a cb
    5348             :  * @return transaction status code
    5349             :  */
    5350             : static enum GNUNET_DB_QueryStatus
    5351           1 : postgres_select_refunds_above_serial_id (void *cls,
    5352             :                                          struct TALER_EXCHANGEDB_Session *session,
    5353             :                                          uint64_t serial_id,
    5354             :                                          TALER_EXCHANGEDB_RefundCallback cb,
    5355             :                                          void *cb_cls)
    5356             : {
    5357           1 :   struct GNUNET_PQ_QueryParam params[] = {
    5358             :     GNUNET_PQ_query_param_uint64 (&serial_id),
    5359             :     GNUNET_PQ_query_param_end
    5360             :   };
    5361           1 :   struct RefundsSerialContext rsc = {
    5362             :     .cb = cb,
    5363             :     .cb_cls = cb_cls,
    5364             :     .status = GNUNET_OK
    5365             :   };
    5366             :   enum GNUNET_DB_QueryStatus qs;
    5367             : 
    5368           1 :   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    5369             :                                              "audit_get_refunds_incr",
    5370             :                                              params,
    5371             :                                              &refunds_serial_helper_cb,
    5372             :                                              &rsc);
    5373           1 :   if (GNUNET_OK != rsc.status)
    5374           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    5375           1 :   return qs;
    5376             : }
    5377             : 
    5378             : 
    5379             : /**
    5380             :  * Closure for #reserves_in_serial_helper_cb().
    5381             :  */
    5382             : struct ReservesInSerialContext
    5383             : {
    5384             : 
    5385             :   /**
    5386             :    * Callback to call.
    5387             :    */
    5388             :   TALER_EXCHANGEDB_ReserveInCallback cb;
    5389             :   
    5390             :   /**
    5391             :    * Closure for @e cb.
    5392             :    */
    5393             :   void *cb_cls;
    5394             :   
    5395             :   /**
    5396             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    5397             :    */ 
    5398             :   int status;
    5399             : };
    5400             : 
    5401             : 
    5402             : /**
    5403             :  * Helper function to be called with the results of a SELECT statement
    5404             :  * that has returned @a num_results results.
    5405             :  *
    5406             :  * @param cls closure of type `struct ReservesInSerialContext`
    5407             :  * @param result the postgres result
    5408             :  * @param num_result the number of results in @a result
    5409             :  */
    5410             : static void
    5411           1 : reserves_in_serial_helper_cb (void *cls,
    5412             :                               PGresult *result,
    5413             :                               unsigned int num_results)
    5414             : {
    5415           1 :   struct ReservesInSerialContext *risc = cls;
    5416             : 
    5417           3 :   for (unsigned int i=0;i<num_results;i++)
    5418             :   {
    5419             :     struct TALER_ReservePublicKeyP reserve_pub;
    5420             :     struct TALER_Amount credit;
    5421             :     json_t *sender_account_details;
    5422             :     struct GNUNET_TIME_Absolute execution_date;
    5423             :     uint64_t rowid;
    5424             :     void *wire_reference;
    5425             :     size_t wire_reference_size;
    5426           2 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5427             :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    5428             :                                             &reserve_pub),
    5429             :       GNUNET_PQ_result_spec_variable_size ("wire_reference",
    5430             :                                            &wire_reference,
    5431             :                                            &wire_reference_size),
    5432             :       TALER_PQ_result_spec_amount ("credit",
    5433             :                                    &credit),
    5434             :       GNUNET_PQ_result_spec_absolute_time("execution_date",
    5435             :                                           &execution_date),
    5436             :       TALER_PQ_result_spec_json ("sender_account_details",
    5437             :                                  &sender_account_details),
    5438             :       GNUNET_PQ_result_spec_uint64 ("reserve_in_serial_id",
    5439             :                                     &rowid),
    5440             :       GNUNET_PQ_result_spec_end
    5441             :     };
    5442             :     int ret;
    5443             : 
    5444           2 :     if (GNUNET_OK !=
    5445           2 :         GNUNET_PQ_extract_result (result,
    5446             :                                   rs,
    5447             :                                   i))
    5448             :     {
    5449           0 :       GNUNET_break (0);
    5450           0 :       risc->status = GNUNET_SYSERR;
    5451           0 :       return;
    5452             :     }
    5453           2 :     ret = risc->cb (risc->cb_cls,
    5454             :                     rowid,
    5455             :                     &reserve_pub,
    5456             :                     &credit,
    5457             :                     sender_account_details,
    5458             :                     wire_reference,
    5459             :                     wire_reference_size,
    5460             :                     execution_date);
    5461           2 :     GNUNET_PQ_cleanup_result (rs);
    5462           2 :     if (GNUNET_OK != ret)
    5463           0 :       break;
    5464             :   }
    5465             : }
    5466             : 
    5467             : 
    5468             : /**
    5469             :  * Select inbound wire transfers into reserves_in above @a serial_id
    5470             :  * in monotonically increasing order.
    5471             :  *
    5472             :  * @param cls closure
    5473             :  * @param session database connection
    5474             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    5475             :  * @param cb function to call on each result
    5476             :  * @param cb_cls closure for @a cb
    5477             :  * @return transaction status code
    5478             :  */
    5479             : static enum GNUNET_DB_QueryStatus
    5480           1 : postgres_select_reserves_in_above_serial_id (void *cls,
    5481             :                                              struct TALER_EXCHANGEDB_Session *session,
    5482             :                                              uint64_t serial_id,
    5483             :                                              TALER_EXCHANGEDB_ReserveInCallback cb,
    5484             :                                              void *cb_cls)
    5485             : {
    5486           1 :   struct GNUNET_PQ_QueryParam params[] = {
    5487             :     GNUNET_PQ_query_param_uint64 (&serial_id),
    5488             :     GNUNET_PQ_query_param_end
    5489             :   };
    5490           1 :   struct ReservesInSerialContext risc = {
    5491             :     .cb = cb,
    5492             :     .cb_cls = cb_cls,
    5493             :     .status = GNUNET_OK
    5494             :   };
    5495             :   enum GNUNET_DB_QueryStatus qs;
    5496             :   
    5497           1 :   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    5498             :                                              "audit_reserves_in_get_transactions_incr",
    5499             :                                              params,
    5500             :                                              &reserves_in_serial_helper_cb,
    5501             :                                              &risc);
    5502           1 :   if (GNUNET_OK != risc.status)
    5503           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    5504           1 :   return qs;
    5505             : }
    5506             : 
    5507             : 
    5508             : /**
    5509             :  * Closure for #reserves_out_serial_helper_cb().
    5510             :  */
    5511             : struct ReservesOutSerialContext
    5512             : {
    5513             : 
    5514             :   /**
    5515             :    * Callback to call.
    5516             :    */
    5517             :   TALER_EXCHANGEDB_WithdrawCallback cb;
    5518             :   
    5519             :   /**
    5520             :    * Closure for @e cb.
    5521             :    */
    5522             :   void *cb_cls;
    5523             :   
    5524             :   /**
    5525             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    5526             :    */ 
    5527             :   int status;
    5528             : };
    5529             : 
    5530             : 
    5531             : /**
    5532             :  * Helper function to be called with the results of a SELECT statement
    5533             :  * that has returned @a num_results results.
    5534             :  *
    5535             :  * @param cls closure of type `struct ReservesOutSerialContext`
    5536             :  * @param result the postgres result
    5537             :  * @param num_result the number of results in @a result
    5538             :  */
    5539             : static void
    5540           1 : reserves_out_serial_helper_cb (void *cls,
    5541             :                                PGresult *result,
    5542             :                                unsigned int num_results)
    5543             : {
    5544           1 :   struct ReservesOutSerialContext *rosc = cls;
    5545             :   
    5546           2 :   for (unsigned int i=0;i<num_results;i++)
    5547             :   {
    5548             :     struct GNUNET_HashCode h_blind_ev;
    5549             :     struct TALER_DenominationPublicKey denom_pub;
    5550             :     struct TALER_DenominationSignature denom_sig;
    5551             :     struct TALER_ReservePublicKeyP reserve_pub;
    5552             :     struct TALER_ReserveSignatureP reserve_sig;
    5553             :     struct GNUNET_TIME_Absolute execution_date;
    5554             :     struct TALER_Amount amount_with_fee;
    5555             :     uint64_t rowid;
    5556           1 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5557             :       GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
    5558             :                                             &h_blind_ev),
    5559             :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    5560             :                                             &denom_pub.rsa_public_key),
    5561             :       GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    5562             :                                            &denom_sig.rsa_signature),
    5563             :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    5564             :                                             &reserve_pub),
    5565             :       GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
    5566             :                                             &reserve_sig),
    5567             :       GNUNET_PQ_result_spec_absolute_time ("execution_date",
    5568             :                                            &execution_date),
    5569             :       TALER_PQ_result_spec_amount ("amount_with_fee",
    5570             :                                    &amount_with_fee),
    5571             :       GNUNET_PQ_result_spec_uint64 ("reserve_out_serial_id",
    5572             :                                     &rowid),
    5573             :       GNUNET_PQ_result_spec_end
    5574             :     };
    5575             :     int ret;
    5576             :     
    5577           1 :     if (GNUNET_OK !=
    5578           1 :         GNUNET_PQ_extract_result (result,
    5579             :                                   rs,
    5580             :                                   i))
    5581             :     {
    5582           0 :       GNUNET_break (0);
    5583           0 :       rosc->status = GNUNET_SYSERR;
    5584           0 :       return;
    5585             :     }
    5586           1 :     ret = rosc->cb (rosc->cb_cls,
    5587             :                     rowid,
    5588             :                     &h_blind_ev,
    5589             :                     &denom_pub,
    5590             :                     &denom_sig,
    5591             :                     &reserve_pub,
    5592             :                     &reserve_sig,
    5593             :                     execution_date,
    5594             :                     &amount_with_fee);
    5595           1 :     GNUNET_PQ_cleanup_result (rs);
    5596           1 :     if (GNUNET_OK != ret)
    5597           0 :       break;
    5598             :   }
    5599             : }
    5600             : 
    5601             : 
    5602             : /**
    5603             :  * Select withdraw operations from reserves_out above @a serial_id
    5604             :  * in monotonically increasing order.
    5605             :  *
    5606             :  * @param cls closure
    5607             :  * @param session database connection
    5608             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    5609             :  * @param cb function to call on each result
    5610             :  * @param cb_cls closure for @a cb
    5611             :  * @return transaction status code
    5612             :  */
    5613             : static enum GNUNET_DB_QueryStatus
    5614           1 : postgres_select_reserves_out_above_serial_id (void *cls,
    5615             :                                               struct TALER_EXCHANGEDB_Session *session,
    5616             :                                               uint64_t serial_id,
    5617             :                                               TALER_EXCHANGEDB_WithdrawCallback cb,
    5618             :                                               void *cb_cls)
    5619             : {
    5620           1 :   struct GNUNET_PQ_QueryParam params[] = {
    5621             :     GNUNET_PQ_query_param_uint64 (&serial_id),
    5622             :     GNUNET_PQ_query_param_end
    5623             :   };
    5624           1 :   struct ReservesOutSerialContext rosc = {
    5625             :     .cb = cb,
    5626             :     .cb_cls = cb_cls,
    5627             :     .status = GNUNET_OK
    5628             :   };
    5629             :   enum GNUNET_DB_QueryStatus qs;
    5630             : 
    5631           1 :   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    5632             :                                              "audit_get_reserves_out_incr",
    5633             :                                              params,
    5634             :                                              &reserves_out_serial_helper_cb,
    5635             :                                              &rosc);
    5636           1 :   if (GNUNET_OK != rosc.status)
    5637           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    5638           1 :   return qs;
    5639             : }
    5640             : 
    5641             : 
    5642             : /**
    5643             :  * Closure for #wire_out_serial_helper_cb().
    5644             :  */
    5645             : struct WireOutSerialContext
    5646             : {
    5647             : 
    5648             :   /**
    5649             :    * Callback to call.
    5650             :    */
    5651             :   TALER_EXCHANGEDB_WireTransferOutCallback cb;
    5652             :   
    5653             :   /**
    5654             :    * Closure for @e cb.
    5655             :    */
    5656             :   void *cb_cls;
    5657             :   
    5658             :   /**
    5659             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    5660             :    */ 
    5661             :   int status;
    5662             : };
    5663             : 
    5664             : 
    5665             : /**
    5666             :  * Helper function to be called with the results of a SELECT statement
    5667             :  * that has returned @a num_results results.
    5668             :  *
    5669             :  * @param cls closure of type `struct WireOutSerialContext`
    5670             :  * @param result the postgres result
    5671             :  * @param num_result the number of results in @a result
    5672             :  */
    5673             : static void
    5674           1 : wire_out_serial_helper_cb (void *cls,
    5675             :                            PGresult *result,
    5676             :                            unsigned int num_results)
    5677             : {
    5678           1 :   struct WireOutSerialContext *wosc = cls;
    5679             :   
    5680           2 :   for (unsigned int i=0;i<num_results;i++)
    5681             :   {
    5682             :     uint64_t rowid;
    5683             :     struct GNUNET_TIME_Absolute date;
    5684             :     struct TALER_WireTransferIdentifierRawP wtid;
    5685             :     json_t *wire;
    5686             :     struct TALER_Amount amount;
    5687           1 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5688             :       GNUNET_PQ_result_spec_uint64 ("wireout_uuid",
    5689             :                                     &rowid),
    5690             :       GNUNET_PQ_result_spec_absolute_time ("execution_date",
    5691             :                                            &date),
    5692             :       GNUNET_PQ_result_spec_auto_from_type ("wtid_raw",
    5693             :                                             &wtid),
    5694             :       TALER_PQ_result_spec_json ("wire_target",
    5695             :                                  &wire),
    5696             :       TALER_PQ_result_spec_amount ("amount",
    5697             :                                    &amount),
    5698             :       GNUNET_PQ_result_spec_end
    5699             :     };
    5700             :     int ret;
    5701             : 
    5702           1 :     if (GNUNET_OK !=
    5703           1 :         GNUNET_PQ_extract_result (result,
    5704             :                                   rs,
    5705             :                                   i))
    5706             :     {
    5707           0 :       GNUNET_break (0);
    5708           0 :       wosc->status = GNUNET_SYSERR;
    5709           0 :       return;
    5710             :     }
    5711           1 :     ret = wosc->cb (wosc->cb_cls,
    5712             :                     rowid,
    5713             :                     date,
    5714             :                     &wtid,
    5715             :                     wire,
    5716             :                     &amount);
    5717           1 :     GNUNET_PQ_cleanup_result (rs);
    5718           1 :     if (GNUNET_OK != ret)
    5719           0 :       break;
    5720             :   }
    5721             : }
    5722             : 
    5723             : 
    5724             : /**
    5725             :  * Function called to select all wire transfers the exchange
    5726             :  * executed.
    5727             :  *
    5728             :  * @param cls closure
    5729             :  * @param session database connection
    5730             :  * @param serial_id highest serial ID to exclude (select strictly larger)
    5731             :  * @param cb function to call for ONE unfinished item
    5732             :  * @param cb_cls closure for @a cb
    5733             :  * @return transaction status code
    5734             :  */
    5735             : static enum GNUNET_DB_QueryStatus
    5736           1 : postgres_select_wire_out_above_serial_id (void *cls,
    5737             :                                           struct TALER_EXCHANGEDB_Session *session,
    5738             :                                           uint64_t serial_id,
    5739             :                                           TALER_EXCHANGEDB_WireTransferOutCallback cb,
    5740             :                                           void *cb_cls)
    5741             : {
    5742           1 :   struct GNUNET_PQ_QueryParam params[] = {
    5743             :     GNUNET_PQ_query_param_uint64 (&serial_id),
    5744             :     GNUNET_PQ_query_param_end
    5745             :   };
    5746           1 :   struct WireOutSerialContext wosc = {
    5747             :     .cb = cb,
    5748             :     .cb_cls = cb_cls,
    5749             :     .status = GNUNET_OK
    5750             :   };
    5751             :   enum GNUNET_DB_QueryStatus qs;
    5752             : 
    5753           1 :   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    5754             :                                              "audit_get_wire_incr",
    5755             :                                              params,
    5756             :                                              &wire_out_serial_helper_cb,
    5757             :                                              &wosc);
    5758           1 :   if (GNUNET_OK != wosc.status)
    5759           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    5760           1 :   return qs;
    5761             : }
    5762             : 
    5763             : 
    5764             : /**
    5765             :  * Closure for #payback_serial_helper_cb().
    5766             :  */
    5767             : struct PaybackSerialContext
    5768             : {
    5769             : 
    5770             :   /**
    5771             :    * Callback to call.
    5772             :    */
    5773             :   TALER_EXCHANGEDB_PaybackCallback cb;
    5774             :   
    5775             :   /**
    5776             :    * Closure for @e cb.
    5777             :    */
    5778             :   void *cb_cls;
    5779             :   
    5780             :   /**
    5781             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    5782             :    */ 
    5783             :   int status;
    5784             : };
    5785             : 
    5786             : 
    5787             : /**
    5788             :  * Helper function to be called with the results of a SELECT statement
    5789             :  * that has returned @a num_results results.
    5790             :  *
    5791             :  * @param cls closure of type `struct PaybackSerialContext`
    5792             :  * @param result the postgres result
    5793             :  * @param num_result the number of results in @a result
    5794             :  */
    5795             : static void
    5796           2 : payback_serial_helper_cb (void *cls,
    5797             :                           PGresult *result,
    5798             :                           unsigned int num_results)
    5799             : {
    5800           2 :   struct PaybackSerialContext *psc = cls;
    5801             :   
    5802           3 :   for (unsigned int i=0;i<num_results;i++)
    5803             :   {
    5804             :     uint64_t rowid;
    5805             :     struct TALER_ReservePublicKeyP reserve_pub;
    5806             :     struct TALER_CoinPublicInfo coin;
    5807             :     struct TALER_CoinSpendSignatureP coin_sig;
    5808             :     struct TALER_DenominationBlindingKeyP coin_blind;
    5809             :     struct TALER_Amount amount;
    5810             :     struct GNUNET_HashCode h_blind_ev;
    5811             :     struct GNUNET_TIME_Absolute timestamp;
    5812           1 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5813             :       GNUNET_PQ_result_spec_uint64 ("payback_uuid",
    5814             :                                     &rowid),
    5815             :       GNUNET_PQ_result_spec_absolute_time ("timestamp",
    5816             :                                            &timestamp),
    5817             :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    5818             :                                             &reserve_pub),
    5819             :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    5820             :                                             &coin.coin_pub),
    5821             :       GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
    5822             :                                             &coin_sig),
    5823             :       GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
    5824             :                                             &coin_blind),
    5825             :       GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
    5826             :                                             &h_blind_ev),
    5827             :       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
    5828             :                                             &coin.denom_pub.rsa_public_key),
    5829             :       GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
    5830             :                                            &coin.denom_sig.rsa_signature),
    5831             :       TALER_PQ_result_spec_amount ("amount",
    5832             :                                    &amount),
    5833             :       GNUNET_PQ_result_spec_end
    5834             :     };
    5835             :     int ret;
    5836             : 
    5837           1 :     if (GNUNET_OK !=
    5838           1 :         GNUNET_PQ_extract_result (result,
    5839             :                                   rs,
    5840             :                                   i))
    5841             :     {
    5842           0 :       GNUNET_break (0);
    5843           0 :       psc->status = GNUNET_SYSERR;
    5844           0 :       return;
    5845             :     }
    5846           1 :     ret = psc->cb (psc->cb_cls,
    5847             :                    rowid,
    5848             :                    timestamp,
    5849             :                    &amount,
    5850             :                    &reserve_pub,
    5851             :                    &coin,
    5852             :                    &coin_sig,
    5853             :                    &coin_blind);
    5854           1 :     GNUNET_PQ_cleanup_result (rs);
    5855           1 :     if (GNUNET_OK != ret)
    5856           0 :       break;
    5857             :   }
    5858             : }
    5859             : 
    5860             : 
    5861             : /**
    5862             :  * Function called to select payback requests the exchange
    5863             :  * received, ordered by serial ID (monotonically increasing).
    5864             :  *
    5865             :  * @param cls closure
    5866             :  * @param session database connection
    5867             :  * @param serial_id lowest serial ID to include (select larger or equal)
    5868             :  * @param cb function to call for ONE unfinished item
    5869             :  * @param cb_cls closure for @a cb
    5870             :  * @return transaction status code
    5871             :  */
    5872             : static enum GNUNET_DB_QueryStatus
    5873           2 : postgres_select_payback_above_serial_id (void *cls,
    5874             :                                          struct TALER_EXCHANGEDB_Session *session,
    5875             :                                          uint64_t serial_id,
    5876             :                                          TALER_EXCHANGEDB_PaybackCallback cb,
    5877             :                                          void *cb_cls)
    5878             : {
    5879           2 :   struct GNUNET_PQ_QueryParam params[] = {
    5880             :     GNUNET_PQ_query_param_uint64 (&serial_id),
    5881             :     GNUNET_PQ_query_param_end
    5882             :   };
    5883           2 :   struct PaybackSerialContext psc = {
    5884             :     .cb = cb,
    5885             :     .cb_cls = cb_cls,
    5886             :     .status = GNUNET_OK
    5887             :   };
    5888             :   enum GNUNET_DB_QueryStatus qs;
    5889             :   
    5890           2 :   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    5891             :                                              "payback_get_incr",
    5892             :                                              params,
    5893             :                                              &payback_serial_helper_cb,
    5894             :                                              &psc);
    5895           2 :   if (GNUNET_OK != psc.status)
    5896           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    5897           2 :   return qs;
    5898             : }
    5899             : 
    5900             : 
    5901             : /**
    5902             :  * Closure for #reserve_closed_serial_helper_cb().
    5903             :  */
    5904             : struct ReserveClosedSerialContext
    5905             : {
    5906             : 
    5907             :   /**
    5908             :    * Callback to call.
    5909             :    */
    5910             :   TALER_EXCHANGEDB_ReserveClosedCallback cb;
    5911             :   
    5912             :   /**
    5913             :    * Closure for @e cb.
    5914             :    */
    5915             :   void *cb_cls;
    5916             :   
    5917             :   /**
    5918             :    * Status code, set to #GNUNET_SYSERR on hard errors.
    5919             :    */ 
    5920             :   int status;
    5921             : };
    5922             : 
    5923             : 
    5924             : /**
    5925             :  * Helper function to be called with the results of a SELECT statement
    5926             :  * that has returned @a num_results results.
    5927             :  *
    5928             :  * @param cls closure of type `struct ReserveClosedSerialContext`
    5929             :  * @param result the postgres result
    5930             :  * @param num_result the number of results in @a result
    5931             :  */
    5932             : static void
    5933           0 : reserve_closed_serial_helper_cb (void *cls,
    5934             :                                  PGresult *result,
    5935             :                                  unsigned int num_results)
    5936             : {
    5937           0 :   struct ReserveClosedSerialContext *rcsc = cls;
    5938             :   
    5939           0 :   for (unsigned int i=0;i<num_results;i++)
    5940             :   {
    5941             :     uint64_t rowid;
    5942             :     struct TALER_ReservePublicKeyP reserve_pub;
    5943             :     json_t *receiver_account;
    5944             :     struct TALER_WireTransferIdentifierRawP wtid;
    5945             :     struct TALER_Amount amount_with_fee;
    5946             :     struct TALER_Amount closing_fee;
    5947             :     struct GNUNET_TIME_Absolute execution_date;
    5948           0 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5949             :       GNUNET_PQ_result_spec_uint64 ("close_uuid",
    5950             :                                     &rowid),
    5951             :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    5952             :                                             &reserve_pub),
    5953             :       GNUNET_PQ_result_spec_absolute_time ("execution_date",
    5954             :                                            &execution_date),
    5955             :       GNUNET_PQ_result_spec_auto_from_type ("wtid",
    5956             :                                             &wtid),
    5957             :       TALER_PQ_result_spec_json ("receiver_account",
    5958             :                                  &receiver_account),
    5959             :       TALER_PQ_result_spec_amount ("amount",
    5960             :                                    &amount_with_fee),
    5961             :       TALER_PQ_result_spec_amount ("closing_fee",
    5962             :                                    &closing_fee),
    5963             :       GNUNET_PQ_result_spec_end
    5964             :     };
    5965             :     int ret;
    5966             : 
    5967           0 :     if (GNUNET_OK !=
    5968           0 :         GNUNET_PQ_extract_result (result,
    5969             :                                   rs,
    5970             :                                   i))
    5971             :     {
    5972           0 :       GNUNET_break (0);
    5973           0 :       rcsc->status = GNUNET_SYSERR;
    5974           0 :       return;
    5975             :     }
    5976           0 :     ret = rcsc->cb (rcsc->cb_cls,
    5977             :                     rowid,
    5978             :                     execution_date,
    5979             :                     &amount_with_fee,
    5980             :                     &closing_fee,
    5981             :                     &reserve_pub,
    5982             :                     receiver_account,
    5983             :                     &wtid);
    5984           0 :     GNUNET_PQ_cleanup_result (rs);
    5985           0 :     if (GNUNET_OK != ret)
    5986           0 :       break;
    5987             :   }
    5988             : }
    5989             : 
    5990             : 
    5991             : /**
    5992             :  * Function called to select reserve close operations the aggregator
    5993             :  * triggered, ordered by serial ID (monotonically increasing).
    5994             :  *
    5995             :  * @param cls closure
    5996             :  * @param session database connection
    5997             :  * @param serial_id lowest serial ID to include (select larger or equal)
    5998             :  * @param cb function to call for ONE unfinished item
    5999             :  * @param cb_cls closure for @a cb
    6000             :  * @return transaction status code
    6001             :  */
    6002             : static enum GNUNET_DB_QueryStatus
    6003           0 : postgres_select_reserve_closed_above_serial_id (void *cls,
    6004             :                                                 struct TALER_EXCHANGEDB_Session *session,
    6005             :                                                 uint64_t serial_id,
    6006             :                                                 TALER_EXCHANGEDB_ReserveClosedCallback cb,
    6007             :                                                 void *cb_cls)
    6008             : {
    6009           0 :   struct GNUNET_PQ_QueryParam params[] = {
    6010             :     GNUNET_PQ_query_param_uint64 (&serial_id),
    6011             :     GNUNET_PQ_query_param_end
    6012             :   };
    6013           0 :   struct ReserveClosedSerialContext rcsc = {
    6014             :     .cb = cb,
    6015             :     .cb_cls = cb_cls,
    6016             :     .status = GNUNET_OK
    6017             :   };
    6018             :   enum GNUNET_DB_QueryStatus qs;
    6019             :   
    6020           0 :   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
    6021             :                                              "reserves_close_get_incr",
    6022             :                                              params,
    6023             :                                              &reserve_closed_serial_helper_cb,
    6024             :                                              &rcsc);
    6025           0 :   if (GNUNET_OK != rcsc.status)
    6026           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    6027           0 :   return qs;
    6028             : }
    6029             : 
    6030             : 
    6031             : /**
    6032             :  * Function called to add a request for an emergency payback for a
    6033             :  * coin.  The funds are to be added back to the reserve.  The function
    6034             :  * should return the @a deadline by which the exchange will trigger a
    6035             :  * wire transfer back to the customer's account for the reserve.
    6036             :  *
    6037             :  * @param cls closure
    6038             :  * @param session database connection
    6039             :  * @param reserve_pub public key of the reserve that is being refunded
    6040             :  * @param coin information about the coin
    6041             :  * @param coin_sig signature of the coin of type #TALER_SIGNATURE_WALLET_COIN_PAYBACK
    6042             :  * @param coin_blind blinding key of the coin
    6043             :  * @param amount total amount to be paid back
    6044             :  * @param h_blind_ev hash of the blinded coin's envelope (must match reserves_out entry)
    6045             :  * @param timestamp current time (rounded)
    6046             :  * @return transaction result status
    6047             :  */
    6048             : static enum GNUNET_DB_QueryStatus
    6049           4 : postgres_insert_payback_request (void *cls,
    6050             :                                  struct TALER_EXCHANGEDB_Session *session,
    6051             :                                  const struct TALER_ReservePublicKeyP *reserve_pub,
    6052             :                                  const struct TALER_CoinPublicInfo *coin,
    6053             :                                  const struct TALER_CoinSpendSignatureP *coin_sig,
    6054             :                                  const struct TALER_DenominationBlindingKeyP *coin_blind,
    6055             :                                  const struct TALER_Amount *amount,
    6056             :                                  const struct GNUNET_HashCode *h_blind_ev,
    6057             :                                  struct GNUNET_TIME_Absolute timestamp)
    6058             : {
    6059           4 :   struct PostgresClosure *pg = cls;
    6060             :   struct GNUNET_TIME_Absolute expiry;
    6061             :   struct TALER_EXCHANGEDB_Reserve reserve;
    6062           4 :   struct GNUNET_PQ_QueryParam params[] = {
    6063           4 :     GNUNET_PQ_query_param_auto_from_type (&coin->coin_pub),
    6064             :     GNUNET_PQ_query_param_auto_from_type (coin_sig),
    6065             :     GNUNET_PQ_query_param_auto_from_type (coin_blind),
    6066             :     TALER_PQ_query_param_amount (amount),
    6067             :     GNUNET_PQ_query_param_absolute_time (&timestamp),
    6068             :     GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
    6069             :     GNUNET_PQ_query_param_end
    6070             :   };
    6071             :   enum GNUNET_DB_QueryStatus qs;
    6072             : 
    6073             :   /* check if the coin is already known */
    6074           4 :   if (0 > (qs = ensure_coin_known (cls,
    6075             :                                    session,
    6076             :                                    coin)))
    6077           0 :     return qs;
    6078             :   /* now store actual payback information */
    6079           4 :   qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
    6080             :                                            "payback_insert",
    6081             :                                            params);
    6082           4 :   if (0 > qs)
    6083             :   {
    6084           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    6085           0 :     return qs;
    6086             :   }
    6087             : 
    6088             :   /* Update reserve balance */
    6089           4 :   reserve.pub = *reserve_pub;
    6090           4 :   qs = postgres_reserve_get (cls,
    6091             :                              session,
    6092             :                              &reserve);
    6093           4 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    6094             :   {
    6095           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    6096           0 :     return qs;
    6097             :   }
    6098           4 :   if (GNUNET_SYSERR ==
    6099           4 :       TALER_amount_add (&reserve.balance,
    6100             :                         &reserve.balance,
    6101             :                         amount))
    6102             :   {
    6103           0 :     GNUNET_break (0);
    6104           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    6105             :   }
    6106           4 :   expiry = GNUNET_TIME_absolute_add (timestamp,
    6107             :                                      pg->idle_reserve_expiration_time);
    6108           4 :   reserve.expiry = GNUNET_TIME_absolute_max (expiry,
    6109             :                                              reserve.expiry);
    6110           4 :   qs = reserves_update (cls,
    6111             :                          session,
    6112             :                          &reserve);
    6113           4 :   if (0 >= qs)
    6114             :   {
    6115           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    6116           0 :     return qs;
    6117             :   }
    6118           4 :   return qs;
    6119             : }
    6120             : 
    6121             : 
    6122             : /**
    6123             :  * Obtain information about which reserve a coin was generated
    6124             :  * from given the hash of the blinded coin.
    6125             :  *
    6126             :  * @param cls closure
    6127             :  * @param session a session
    6128             :  * @param h_blind_ev hash of the blinded coin
    6129             :  * @param[out] reserve_pub set to information about the reserve (on success only)
    6130             :  * @return transaction status code
    6131             :  */
    6132             : static enum GNUNET_DB_QueryStatus
    6133           4 : postgres_get_reserve_by_h_blind (void *cls,
    6134             :                                  struct TALER_EXCHANGEDB_Session *session,
    6135             :                                  const struct GNUNET_HashCode *h_blind_ev,
    6136             :                                  struct TALER_ReservePublicKeyP *reserve_pub)
    6137             : {
    6138           4 :   struct GNUNET_PQ_QueryParam params[] = {
    6139             :     GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
    6140             :     GNUNET_PQ_query_param_end
    6141             :   };
    6142           4 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6143             :     GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    6144             :                                           reserve_pub),
    6145             :     GNUNET_PQ_result_spec_end
    6146             :   };
    6147             : 
    6148           4 :   return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    6149             :                                                    "reserve_by_h_blind",
    6150             :                                                    params,
    6151             :                                                    rs);
    6152             : }
    6153             : 
    6154             : 
    6155             : /**
    6156             :  * Store information that a denomination key was revoked
    6157             :  * in the database.
    6158             :  *
    6159             :  * @param cls closure
    6160             :  * @param session a session
    6161             :  * @param denom_pub_hash hash of the revoked denomination key
    6162             :  * @param master_sig signature affirming the revocation
    6163             :  * @return transaction status code
    6164             :  */
    6165             : static enum GNUNET_DB_QueryStatus
    6166           5 : postgres_insert_denomination_revocation (void *cls,
    6167             :                                          struct TALER_EXCHANGEDB_Session *session,
    6168             :                                          const struct GNUNET_HashCode *denom_pub_hash,
    6169             :                                          const struct TALER_MasterSignatureP *master_sig)
    6170             : {
    6171           5 :   struct GNUNET_PQ_QueryParam params[] = {
    6172             :     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
    6173             :     GNUNET_PQ_query_param_auto_from_type (master_sig),
    6174             :     GNUNET_PQ_query_param_end
    6175             :   };
    6176             : 
    6177           5 :   return GNUNET_PQ_eval_prepared_non_select (session->conn,
    6178             :                                              "denomination_revocation_insert",
    6179             :                                              params);
    6180             : }
    6181             : 
    6182             : 
    6183             : /**
    6184             :  * Obtain information about a denomination key's revocation from
    6185             :  * the database.
    6186             :  *
    6187             :  * @param cls closure
    6188             :  * @param session a session
    6189             :  * @param denom_pub_hash hash of the revoked denomination key
    6190             :  * @param[out] master_sig signature affirming the revocation
    6191             :  * @param[out] rowid row where the information is stored
    6192             :  * @return transaction status code
    6193             :  */
    6194             : static enum GNUNET_DB_QueryStatus
    6195           1 : postgres_get_denomination_revocation (void *cls,
    6196             :                                       struct TALER_EXCHANGEDB_Session *session,
    6197             :                                       const struct GNUNET_HashCode *denom_pub_hash,
    6198             :                                       struct TALER_MasterSignatureP *master_sig,
    6199             :                                       uint64_t *rowid)
    6200             : {
    6201           1 :   struct GNUNET_PQ_QueryParam params[] = {
    6202             :     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
    6203             :     GNUNET_PQ_query_param_end
    6204             :   };
    6205           1 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6206             :     GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig),
    6207             :     GNUNET_PQ_result_spec_uint64 ("denom_revocations_serial_id", rowid),
    6208             :     GNUNET_PQ_result_spec_end
    6209             :   };
    6210             : 
    6211           1 :   return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
    6212             :                                                    "denomination_revocation_get",
    6213             :                                                    params,
    6214             :                                                    rs);
    6215             : }
    6216             : 
    6217             : 
    6218             : /**
    6219             :  * Initialize Postgres database subsystem.
    6220             :  *
    6221             :  * @param cls a configuration instance
    6222             :  * @return NULL on error, otherwise a `struct TALER_EXCHANGEDB_Plugin`
    6223             :  */
    6224             : void *
    6225          38 : libtaler_plugin_exchangedb_postgres_init (void *cls)
    6226             : {
    6227          38 :   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
    6228             :   struct PostgresClosure *pg;
    6229             :   struct TALER_EXCHANGEDB_Plugin *plugin;
    6230             :   const char *ec;
    6231             : 
    6232          38 :   pg = GNUNET_new (struct PostgresClosure);
    6233             : 
    6234          38 :   if (0 != pthread_key_create (&pg->db_conn_threadlocal,
    6235             :                                &db_conn_destroy))
    6236             :   {
    6237           0 :     TALER_LOG_ERROR ("Cannnot create pthread key.\n");
    6238           0 :     GNUNET_free (pg);
    6239           0 :     return NULL;
    6240             :   }
    6241          38 :   ec = getenv ("TALER_EXCHANGEDB_POSTGRES_CONFIG");
    6242          38 :   if (NULL != ec)
    6243             :   {
    6244          38 :     pg->connection_cfg_str = GNUNET_strdup (ec);
    6245             :   }
    6246             :   else
    6247             :   {
    6248           0 :     if (GNUNET_OK !=
    6249           0 :         GNUNET_CONFIGURATION_get_value_string (cfg,
    6250             :                                                "exchangedb-postgres",
    6251             :                                                "db_conn_str",
    6252             :                                                &pg->connection_cfg_str))
    6253             :     {
    6254           0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    6255             :                                  "exchangedb-postgres",
    6256             :                                  "db_conn_str");
    6257           0 :       GNUNET_free (pg);
    6258           0 :       return NULL;
    6259             :     }
    6260             :   }
    6261          38 :   if (GNUNET_OK !=
    6262          38 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    6263             :                                            "exchangedb",
    6264             :                                            "IDLE_RESERVE_EXPIRATION_TIME",
    6265             :                                            &pg->idle_reserve_expiration_time))
    6266             :   {
    6267           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    6268             :                                "exchangedb",
    6269             :                                "IDLE_RESERVE_EXPIRATION_TIME");
    6270           0 :     GNUNET_free (pg);
    6271           0 :     return NULL;
    6272             :   }
    6273          38 :   plugin = GNUNET_new (struct TALER_EXCHANGEDB_Plugin);
    6274          38 :   plugin->cls = pg;
    6275          38 :   plugin->get_session = &postgres_get_session;
    6276          38 :   plugin->drop_tables = &postgres_drop_tables;
    6277          38 :   plugin->create_tables = &postgres_create_tables;
    6278          38 :   plugin->start = &postgres_start;
    6279          38 :   plugin->commit = &postgres_commit;
    6280          38 :   plugin->rollback = &postgres_rollback;
    6281          38 :   plugin->insert_denomination_info = &postgres_insert_denomination_info;
    6282          38 :   plugin->get_denomination_info = &postgres_get_denomination_info;
    6283          38 :   plugin->reserve_get = &postgres_reserve_get;
    6284          38 :   plugin->reserves_in_insert = &postgres_reserves_in_insert;
    6285          38 :   plugin->get_latest_reserve_in_reference = &postgres_get_latest_reserve_in_reference;
    6286          38 :   plugin->get_withdraw_info = &postgres_get_withdraw_info;
    6287          38 :   plugin->insert_withdraw_info = &postgres_insert_withdraw_info;
    6288          38 :   plugin->get_reserve_history = &postgres_get_reserve_history;
    6289          38 :   plugin->free_reserve_history = &common_free_reserve_history;
    6290          38 :   plugin->have_deposit = &postgres_have_deposit;
    6291          38 :   plugin->mark_deposit_tiny = &postgres_mark_deposit_tiny;
    6292          38 :   plugin->test_deposit_done = &postgres_test_deposit_done;
    6293          38 :   plugin->mark_deposit_done = &postgres_mark_deposit_done;
    6294          38 :   plugin->get_ready_deposit = &postgres_get_ready_deposit;
    6295          38 :   plugin->iterate_matching_deposits = &postgres_iterate_matching_deposits;
    6296          38 :   plugin->insert_deposit = &postgres_insert_deposit;
    6297          38 :   plugin->insert_refund = &postgres_insert_refund;
    6298          38 :   plugin->get_refresh_session = &postgres_get_refresh_session;
    6299          38 :   plugin->create_refresh_session = &postgres_create_refresh_session;
    6300          38 :   plugin->insert_refresh_order = &postgres_insert_refresh_order;
    6301          38 :   plugin->get_refresh_order = &postgres_get_refresh_order;
    6302          38 :   plugin->insert_refresh_commit_coins = &postgres_insert_refresh_commit_coins;
    6303          38 :   plugin->get_refresh_commit_coins = &postgres_get_refresh_commit_coins;
    6304          38 :   plugin->free_refresh_commit_coins = &postgres_free_refresh_commit_coins;
    6305          38 :   plugin->insert_refresh_transfer_public_key = &postgres_insert_refresh_transfer_public_key;
    6306          38 :   plugin->get_refresh_transfer_public_key = &postgres_get_refresh_transfer_public_key;
    6307          38 :   plugin->get_refresh_out = &postgres_get_refresh_out;
    6308          38 :   plugin->insert_refresh_out = &postgres_insert_refresh_out;
    6309          38 :   plugin->get_link_data_list = &postgres_get_link_data_list;
    6310          38 :   plugin->free_link_data_list = &common_free_link_data_list;
    6311          38 :   plugin->get_transfer = &postgres_get_transfer;
    6312          38 :   plugin->get_coin_transactions = &postgres_get_coin_transactions;
    6313          38 :   plugin->free_coin_transaction_list = &common_free_coin_transaction_list;
    6314          38 :   plugin->lookup_wire_transfer = &postgres_lookup_wire_transfer;
    6315          38 :   plugin->wire_lookup_deposit_wtid = &postgres_wire_lookup_deposit_wtid;
    6316          38 :   plugin->insert_aggregation_tracking = &postgres_insert_aggregation_tracking;
    6317          38 :   plugin->insert_wire_fee = &postgres_insert_wire_fee;
    6318          38 :   plugin->get_wire_fee = &postgres_get_wire_fee;
    6319          38 :   plugin->get_expired_reserves = &postgres_get_expired_reserves;
    6320          38 :   plugin->insert_reserve_closed = &postgres_insert_reserve_closed;
    6321          38 :   plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert;
    6322          38 :   plugin->wire_prepare_data_mark_finished = &postgres_wire_prepare_data_mark_finished;
    6323          38 :   plugin->wire_prepare_data_get = &postgres_wire_prepare_data_get;
    6324          38 :   plugin->start_deferred_wire_out = &postgres_start_deferred_wire_out;
    6325          38 :   plugin->store_wire_transfer_out = &postgres_store_wire_transfer_out;
    6326          38 :   plugin->gc = &postgres_gc;
    6327          38 :   plugin->select_deposits_above_serial_id = &postgres_select_deposits_above_serial_id;
    6328          38 :   plugin->select_refreshs_above_serial_id = &postgres_select_refreshs_above_serial_id;
    6329          38 :   plugin->select_refunds_above_serial_id = &postgres_select_refunds_above_serial_id;
    6330          38 :   plugin->select_reserves_in_above_serial_id = &postgres_select_reserves_in_above_serial_id;
    6331          38 :   plugin->select_reserves_out_above_serial_id = &postgres_select_reserves_out_above_serial_id;
    6332          38 :   plugin->select_wire_out_above_serial_id = &postgres_select_wire_out_above_serial_id;
    6333          38 :   plugin->select_payback_above_serial_id = &postgres_select_payback_above_serial_id;
    6334          38 :   plugin->select_reserve_closed_above_serial_id = &postgres_select_reserve_closed_above_serial_id;
    6335          38 :   plugin->insert_payback_request = &postgres_insert_payback_request;
    6336          38 :   plugin->get_reserve_by_h_blind = &postgres_get_reserve_by_h_blind;
    6337          38 :   plugin->insert_denomination_revocation = &postgres_insert_denomination_revocation;
    6338          38 :   plugin->get_denomination_revocation = &postgres_get_denomination_revocation;
    6339          38 :   return plugin;
    6340             : }
    6341             : 
    6342             : 
    6343             : /**
    6344             :  * Shutdown Postgres database subsystem.
    6345             :  *
    6346             :  * @param cls a `struct TALER_EXCHANGEDB_Plugin`
    6347             :  * @return NULL (always)
    6348             :  */
    6349             : void *
    6350          38 : libtaler_plugin_exchangedb_postgres_done (void *cls)
    6351             : {
    6352          38 :   struct TALER_EXCHANGEDB_Plugin *plugin = cls;
    6353          38 :   struct PostgresClosure *pg = plugin->cls;
    6354             : 
    6355          38 :   GNUNET_free (pg->connection_cfg_str);
    6356          38 :   GNUNET_free (pg);
    6357          38 :   GNUNET_free (plugin);
    6358          38 :   return NULL;
    6359             : }
    6360             : 
    6361             : /* end of plugin_exchangedb_postgres.c */

Generated by: LCOV version 1.13