LCOV - code coverage report
Current view: top level - exchangedb - plugin_exchangedb_postgres.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 962 1216 79.1 %
Date: 2017-11-25 11:31:41 Functions: 91 94 96.8 %

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

Generated by: LCOV version 1.13