LCOV - code coverage report
Current view: top level - backenddb - plugin_merchantdb_postgres.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 563 724 77.8 %
Date: 2018-07-14 06:17:23 Functions: 46 46 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2014-2018 INRIA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Lesser 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             :  * @file merchant/plugin_merchantdb_postgres.c
      18             :  * @brief database helper functions for postgres used by the merchant
      19             :  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
      20             :  * @author Christian Grothoff
      21             :  * @author Marcello Stanisci
      22             :  */
      23             : #include "platform.h"
      24             : #include <gnunet/gnunet_util_lib.h>
      25             : #include <gnunet/gnunet_pq_lib.h>
      26             : #include <taler/taler_util.h>
      27             : #include <taler/taler_pq_lib.h>
      28             : #include <taler/taler_json_lib.h>
      29             : #include "taler_merchantdb_plugin.h"
      30             : 
      31             : /**
      32             :  * How often do we re-try if we run into a DB serialization error?
      33             :  */
      34             : #define MAX_RETRIES 3
      35             : 
      36             : 
      37             : /**
      38             :  * Type of the "cls" argument given to each of the functions in
      39             :  * our API.
      40             :  */
      41             : struct PostgresClosure
      42             : {
      43             : 
      44             :   /**
      45             :    * Postgres connection handle.
      46             :    */
      47             :   PGconn *conn;
      48             : 
      49             :   /**
      50             :    * Underlying configuration.
      51             :    */
      52             :   const struct GNUNET_CONFIGURATION_Handle *cfg;
      53             : 
      54             :   /**
      55             :    * Name of the currently active transaction, NULL if none is active.
      56             :    */
      57             :   const char *transaction_name;
      58             : 
      59             : };
      60             : 
      61             : 
      62             : /**
      63             :  * Drop merchant tables
      64             :  *
      65             :  * @param cls closure our `struct Plugin`
      66             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
      67             :  */
      68             : static int
      69           5 : postgres_drop_tables (void *cls)
      70             : {
      71           5 :   struct PostgresClosure *pg = cls;
      72           5 :   struct GNUNET_PQ_ExecuteStatement es[] = {
      73             :     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_transfers CASCADE;"),
      74             :     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_deposits CASCADE;"),
      75             :     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_transactions CASCADE;"),
      76             :     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_proofs CASCADE;"),
      77             :     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_contract_terms CASCADE;"),
      78             :     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_refunds CASCADE;"),
      79             :     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS exchange_wire_fees CASCADE;"),
      80             :     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tips CASCADE;"),
      81             :     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tip_pickups CASCADE;"),
      82             :     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tip_reserve_credits CASCADE;"),
      83             :     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tip_reserves CASCADE;"),
      84             :     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_orders CASCADE;"),
      85             :     GNUNET_PQ_EXECUTE_STATEMENT_END
      86             :   };
      87             : 
      88           5 :   return GNUNET_PQ_exec_statements (pg->conn,
      89             :                                     es);
      90             : }
      91             : 
      92             : 
      93             : /**
      94             :  * Initialize merchant tables
      95             :  *
      96             :  * @param cls closure our `struct Plugin`
      97             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
      98             :  */
      99             : static int
     100           5 : postgres_initialize (void *cls)
     101             : {
     102           5 :   struct PostgresClosure *pg = cls;
     103           5 :   struct GNUNET_PQ_ExecuteStatement es[] = {
     104             :     /* Orders created by the frontend, not signed or given a nonce yet.
     105             :        The contract terms will change (nonce will be added) when moved to the
     106             :        contract terms table */
     107             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_orders ("
     108             :                             "order_id VARCHAR NOT NULL"
     109             :                             ",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)"
     110             :                             ",contract_terms BYTEA NOT NULL"
     111             :                             ",timestamp INT8 NOT NULL"
     112             :                             ",PRIMARY KEY (order_id, merchant_pub)"
     113             :                             ");"),
     114             :     /* Offers we made to customers */
     115             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_contract_terms ("
     116             :                             "order_id VARCHAR NOT NULL"
     117             :                             ",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)"
     118             :                             ",contract_terms BYTEA NOT NULL"
     119             :                             ",h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)"
     120             :                             ",timestamp INT8 NOT NULL"
     121             :                             ",row_id BIGSERIAL UNIQUE"
     122             :                             ",paid boolean DEFAULT FALSE NOT NULL"
     123             :                             ",last_session_id VARCHAR DEFAULT '' NOT NULL"
     124             :                             ",PRIMARY KEY (order_id, merchant_pub)"
     125             :                             ",UNIQUE (h_contract_terms, merchant_pub)"
     126             :                             ");"),
     127             :     /* Table with the proofs for each coin we deposited at the exchange */
     128             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_deposits ("
     129             :                             " h_contract_terms BYTEA NOT NULL"
     130             :                             ",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)"
     131             :                             ",coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)"
     132             :                             ",exchange_url VARCHAR NOT NULL"
     133             :                             ",amount_with_fee_val INT8 NOT NULL"
     134             :                             ",amount_with_fee_frac INT4 NOT NULL"
     135             :                             ",amount_with_fee_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
     136             :                             ",deposit_fee_val INT8 NOT NULL"
     137             :                             ",deposit_fee_frac INT4 NOT NULL"
     138             :                             ",deposit_fee_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
     139             :                             ",refund_fee_val INT8 NOT NULL"
     140             :                             ",refund_fee_frac INT4 NOT NULL"
     141             :                             ",refund_fee_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
     142             :                             ",wire_fee_val INT8 NOT NULL"
     143             :                             ",wire_fee_frac INT4 NOT NULL"
     144             :                             ",wire_fee_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
     145             :                             ",signkey_pub BYTEA NOT NULL CHECK (LENGTH(signkey_pub)=32)"
     146             :                             ",exchange_proof BYTEA NOT NULL"
     147             :                             ",PRIMARY KEY (h_contract_terms, coin_pub)"
     148             :                             ",FOREIGN KEY (h_contract_terms, merchant_pub) REFERENCES merchant_contract_terms (h_contract_terms, merchant_pub)"
     149             :                             ");"),
     150             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_proofs ("
     151             :                             " exchange_url VARCHAR NOT NULL"
     152             :                             ",wtid BYTEA CHECK (LENGTH(wtid)=32)"
     153             :                             ",execution_time INT8 NOT NULL"
     154             :                             ",signkey_pub BYTEA NOT NULL CHECK (LENGTH(signkey_pub)=32)"
     155             :                             ",proof BYTEA NOT NULL"
     156             :                             ",PRIMARY KEY (wtid, exchange_url)"
     157             :                             ");"),
     158             :     /* Note that h_contract_terms + coin_pub may actually be unknown to
     159             :        us, e.g. someone else deposits something for us at the exchange.
     160             :        Hence those cannot be foreign keys into deposits/transactions! */
     161             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_transfers ("
     162             :                             " h_contract_terms BYTEA NOT NULL"
     163             :                             ",coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)"
     164             :                             ",wtid BYTEA NOT NULL CHECK (LENGTH(wtid)=32)"
     165             :                             ",PRIMARY KEY (h_contract_terms, coin_pub)"
     166             :                             ");"),
     167             :     GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS merchant_transfers_by_coin"
     168             :                                 " ON merchant_transfers (h_contract_terms, coin_pub)"),
     169             :     GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS merchant_transfers_by_wtid"
     170             :                                 " ON merchant_transfers (wtid)"),
     171             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS exchange_wire_fees ("
     172             :                             " exchange_pub BYTEA NOT NULL CHECK (length(exchange_pub)=32)"
     173             :                             ",h_wire_method BYTEA NOT NULL CHECK (length(h_wire_method)=64)"
     174             :                             ",wire_fee_val INT8 NOT NULL"
     175             :                             ",wire_fee_frac INT4 NOT NULL"
     176             :                             ",wire_fee_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
     177             :                             ",closing_fee_val INT8 NOT NULL"
     178             :                             ",closing_fee_frac INT4 NOT NULL"
     179             :                             ",closing_fee_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
     180             :                             ",start_date INT8 NOT NULL"
     181             :                             ",end_date INT8 NOT NULL"
     182             :                             ",exchange_sig BYTEA NOT NULL CHECK (length(exchange_sig)=64)"
     183             :                             ",PRIMARY KEY (exchange_pub,h_wire_method,start_date,end_date)"
     184             :                             ");"),
     185             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_refunds ("
     186             :                             " rtransaction_id BIGSERIAL UNIQUE"
     187             :                             ",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)"
     188             :                             ",h_contract_terms BYTEA NOT NULL"
     189             :                             ",coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)"
     190             :                             ",reason VARCHAR NOT NULL"
     191             :                             ",refund_amount_val INT8 NOT NULL"
     192             :                             ",refund_amount_frac INT4 NOT NULL"
     193             :                             ",refund_amount_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
     194             :                             ",refund_fee_val INT8 NOT NULL"
     195             :                             ",refund_fee_frac INT4 NOT NULL"
     196             :                             ",refund_fee_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
     197             :                             ");"),
     198             :     /* balances of the reserves available for tips */
     199             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tip_reserves ("
     200             :                             " reserve_priv BYTEA NOT NULL CHECK (LENGTH(reserve_priv)=32)"
     201             :                             ",expiration INT8 NOT NULL"
     202             :                             ",balance_val INT8 NOT NULL"
     203             :                             ",balance_frac INT4 NOT NULL"
     204             :                             ",balance_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
     205             :                             ",PRIMARY KEY (reserve_priv)"
     206             :                             ");"),
     207             :     /* table where we remember when tipping reserves where established / enabled */
     208             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tip_reserve_credits ("
     209             :                             " reserve_priv BYTEA NOT NULL CHECK (LENGTH(reserve_priv)=32)"
     210             :                             ",credit_uuid BYTEA UNIQUE NOT NULL CHECK (LENGTH(credit_uuid)=64)"
     211             :                             ",timestamp INT8 NOT NULL"
     212             :                             ",amount_val INT8 NOT NULL"
     213             :                             ",amount_frac INT4 NOT NULL"
     214             :                             ",amount_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
     215             :                             ",PRIMARY KEY (credit_uuid)"
     216             :                             ");"),
     217             :     /* tips that have been authorized */
     218             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tips ("
     219             :                             " reserve_priv BYTEA NOT NULL CHECK (LENGTH(reserve_priv)=32)"
     220             :                             ",tip_id BYTEA NOT NULL CHECK (LENGTH(tip_id)=64)"
     221             :                             ",exchange_url VARCHAR NOT NULL"
     222             :                             ",justification VARCHAR NOT NULL"
     223             :                             ",timestamp INT8 NOT NULL"
     224             :                             ",amount_val INT8 NOT NULL" /* overall tip amount */
     225             :                             ",amount_frac INT4 NOT NULL"
     226             :                             ",amount_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
     227             :                             ",left_val INT8 NOT NULL" /* tip amount not yet picked up */
     228             :                             ",left_frac INT4 NOT NULL"
     229             :                             ",left_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
     230             :                             ",PRIMARY KEY (tip_id)"
     231             :                             ");"),
     232             :     /* tips that have been picked up */
     233             :     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tip_pickups ("
     234             :                             " tip_id BYTEA NOT NULL REFERENCES merchant_tips (tip_id) ON DELETE CASCADE"
     235             :                             ",pickup_id BYTEA NOT NULL CHECK (LENGTH(pickup_id)=64)"
     236             :                             ",amount_val INT8 NOT NULL"
     237             :                             ",amount_frac INT4 NOT NULL"
     238             :                             ",amount_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
     239             :                             ",PRIMARY KEY (pickup_id)"
     240             :                             ");"),
     241             :     GNUNET_PQ_EXECUTE_STATEMENT_END
     242             :   };
     243           5 :   struct GNUNET_PQ_PreparedStatement ps[] = {
     244             :     GNUNET_PQ_make_prepare ("insert_deposit",
     245             :                             "INSERT INTO merchant_deposits"
     246             :                             "(h_contract_terms"
     247             :                             ",merchant_pub"
     248             :                             ",coin_pub"
     249             :                             ",exchange_url"
     250             :                             ",amount_with_fee_val"
     251             :                             ",amount_with_fee_frac"
     252             :                             ",amount_with_fee_curr"
     253             :                             ",deposit_fee_val"
     254             :                             ",deposit_fee_frac"
     255             :                             ",deposit_fee_curr"
     256             :                             ",refund_fee_val"
     257             :                             ",refund_fee_frac"
     258             :                             ",refund_fee_curr"
     259             :                             ",wire_fee_val"
     260             :                             ",wire_fee_frac"
     261             :                             ",wire_fee_curr"
     262             :                             ",signkey_pub"
     263             :                             ",exchange_proof) VALUES "
     264             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)",
     265             :                             18),
     266             :     GNUNET_PQ_make_prepare ("insert_transfer",
     267             :                             "INSERT INTO merchant_transfers"
     268             :                             "(h_contract_terms"
     269             :                             ",coin_pub"
     270             :                             ",wtid) VALUES "
     271             :                             "($1, $2, $3)",
     272             :                             3),
     273             :     GNUNET_PQ_make_prepare ("insert_refund",
     274             :                             "INSERT INTO merchant_refunds"
     275             :                             "(merchant_pub"
     276             :                             ",h_contract_terms"
     277             :                             ",coin_pub"
     278             :                             ",reason"
     279             :                             ",refund_amount_val"
     280             :                             ",refund_amount_frac"
     281             :                             ",refund_amount_curr"
     282             :                             ",refund_fee_val"
     283             :                             ",refund_fee_frac"
     284             :                             ",refund_fee_curr"
     285             :                             ") VALUES"
     286             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
     287             :                             10),
     288             :     GNUNET_PQ_make_prepare ("insert_proof",
     289             :                             "INSERT INTO merchant_proofs"
     290             :                             "(exchange_url"
     291             :                             ",wtid"
     292             :                             ",execution_time"
     293             :                             ",signkey_pub"
     294             :                             ",proof) VALUES "
     295             :                             "($1, $2, $3, $4, $5)",
     296             :                             5),
     297             :     GNUNET_PQ_make_prepare ("insert_contract_terms",
     298             :                             "INSERT INTO merchant_contract_terms"
     299             :                             "(order_id"
     300             :                             ",merchant_pub"
     301             :                             ",timestamp"
     302             :                             ",contract_terms"
     303             :                             ",h_contract_terms)"
     304             :                             " VALUES "
     305             :                             "($1, $2, $3, $4, $5)",
     306             :                             5),
     307             :     GNUNET_PQ_make_prepare ("insert_order",
     308             :                             "INSERT INTO merchant_orders"
     309             :                             "(order_id"
     310             :                             ",merchant_pub"
     311             :                             ",timestamp"
     312             :                             ",contract_terms)"
     313             :                             " VALUES "
     314             :                             "($1, $2, $3, $4)",
     315             :                             4),
     316             :     GNUNET_PQ_make_prepare ("mark_proposal_paid",
     317             :                             "UPDATE merchant_contract_terms SET"
     318             :                             " paid=TRUE, last_session_id=$3"
     319             :                             " WHERE h_contract_terms=$1"
     320             :                             " AND merchant_pub=$2",
     321             :                             2),
     322             :     GNUNET_PQ_make_prepare ("insert_wire_fee",
     323             :                             "INSERT INTO exchange_wire_fees"
     324             :                             "(exchange_pub"
     325             :                             ",h_wire_method"
     326             :                             ",wire_fee_val"
     327             :                             ",wire_fee_frac"
     328             :                             ",wire_fee_curr"
     329             :                             ",closing_fee_val"
     330             :                             ",closing_fee_frac"
     331             :                             ",closing_fee_curr"
     332             :                             ",start_date"
     333             :                             ",end_date"
     334             :                             ",exchange_sig)"
     335             :                             " VALUES "
     336             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
     337             :                             11),
     338             :     GNUNET_PQ_make_prepare ("lookup_wire_fee",
     339             :                             "SELECT"
     340             :                             " wire_fee_val"
     341             :                             ",wire_fee_frac"
     342             :                             ",wire_fee_curr"
     343             :                             ",closing_fee_val"
     344             :                             ",closing_fee_frac"
     345             :                             ",closing_fee_curr"
     346             :                             ",start_date"
     347             :                             ",end_date"
     348             :                             ",exchange_sig"
     349             :                             " FROM exchange_wire_fees"
     350             :                             " WHERE exchange_pub=$1"
     351             :                             "   AND h_wire_method=$2"
     352             :                             "   AND start_date <= $3"
     353             :                             "   AND end_date > $3",
     354             :                             1),
     355             :     GNUNET_PQ_make_prepare ("find_contract_terms_from_hash",
     356             :                             "SELECT"
     357             :                             " contract_terms"
     358             :                             " FROM merchant_contract_terms"
     359             :                             " WHERE h_contract_terms=$1"
     360             :                             "   AND merchant_pub=$2",
     361             :                             2),
     362             :     GNUNET_PQ_make_prepare ("find_paid_contract_terms_from_hash",
     363             :                             "SELECT"
     364             :                             " contract_terms"
     365             :                             " FROM merchant_contract_terms"
     366             :                             " WHERE h_contract_terms=$1"
     367             :                             "   AND merchant_pub=$2"
     368             :                             "   AND paid=TRUE",
     369             :                             2),
     370             :     GNUNET_PQ_make_prepare ("end_transaction",
     371             :                             "COMMIT",
     372             :                             0),
     373             : 
     374             :     /*NOTE: minimal version, to be expanded on a needed basis*/
     375             :     GNUNET_PQ_make_prepare ("find_refunds",
     376             :                             "SELECT"
     377             :                             " refund_amount_val"
     378             :                             ",refund_amount_frac"
     379             :                             ",refund_amount_curr"
     380             :                             " FROM merchant_refunds"
     381             :                             " WHERE coin_pub=$1",
     382             :                             1),
     383             :     GNUNET_PQ_make_prepare ("find_contract_terms_history",
     384             :                             "SELECT"
     385             :                             " contract_terms"
     386             :                             " FROM merchant_contract_terms"
     387             :                             " WHERE"
     388             :                             " order_id=$1"
     389             :                             " AND merchant_pub=$2"
     390             :                             " AND paid=TRUE",
     391             :                             2),
     392             :     GNUNET_PQ_make_prepare ("find_contract_terms",
     393             :                             "SELECT"
     394             :                             " contract_terms"
     395             :                             ",last_session_id"
     396             :                             " FROM merchant_contract_terms"
     397             :                             " WHERE"
     398             :                             " order_id=$1"
     399             :                             " AND merchant_pub=$2",
     400             :                             2),
     401             :     GNUNET_PQ_make_prepare ("find_order",
     402             :                             "SELECT"
     403             :                             " contract_terms"
     404             :                             " FROM merchant_orders"
     405             :                             " WHERE"
     406             :                             " order_id=$1"
     407             :                             " AND merchant_pub=$2",
     408             :                             2),
     409             :     GNUNET_PQ_make_prepare ("find_contract_terms_by_date",
     410             :                             "SELECT"
     411             :                             " contract_terms"
     412             :                             ",order_id"
     413             :                             ",row_id"
     414             :                             " FROM merchant_contract_terms"
     415             :                             " WHERE"
     416             :                             " timestamp<$1"
     417             :                             " AND merchant_pub=$2"
     418             :                             " AND paid=TRUE"
     419             :                             " ORDER BY row_id DESC, timestamp DESC"
     420             :                             " LIMIT $3",
     421             :                             3),
     422             :     GNUNET_PQ_make_prepare ("find_refunds_from_contract_terms_hash",
     423             :                             "SELECT"
     424             :                             " coin_pub"
     425             :                             ",rtransaction_id"
     426             :                             ",refund_amount_val"
     427             :                             ",refund_amount_frac"
     428             :                             ",refund_amount_curr"
     429             :                             ",refund_fee_val"
     430             :                             ",refund_fee_frac"
     431             :                             ",refund_fee_curr"
     432             :                             ",reason"
     433             :                             " FROM merchant_refunds"
     434             :                             " WHERE merchant_pub=$1"
     435             :                             " AND h_contract_terms=$2",
     436             :                             2),
     437             :     GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range",
     438             :                             "SELECT"
     439             :                             " contract_terms"
     440             :                             ",order_id"
     441             :                             ",row_id"
     442             :                             " FROM merchant_contract_terms"
     443             :                             " WHERE"
     444             :                             " timestamp<$1"
     445             :                             " AND merchant_pub=$2"
     446             :                             " AND row_id<$3"
     447             :                             " AND paid=TRUE"
     448             :                             " ORDER BY row_id DESC, timestamp DESC"
     449             :                             " LIMIT $4",
     450             :                             4),
     451             :     GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range_future",
     452             :                             "SELECT"
     453             :                             " contract_terms"
     454             :                             ",order_id"
     455             :                             ",row_id"
     456             :                             " FROM merchant_contract_terms"
     457             :                             " WHERE"
     458             :                             " timestamp>$1"
     459             :                             " AND merchant_pub=$2"
     460             :                             " AND row_id>$3"
     461             :                             " AND paid=TRUE"
     462             :                             " ORDER BY row_id DESC, timestamp DESC"
     463             :                             " LIMIT $4",
     464             :                             4),
     465             :     GNUNET_PQ_make_prepare ("find_deposits",
     466             :                             "SELECT"
     467             :                             " coin_pub"
     468             :                             ",exchange_url"
     469             :                             ",amount_with_fee_val"
     470             :                             ",amount_with_fee_frac"
     471             :                             ",amount_with_fee_curr"
     472             :                             ",deposit_fee_val"
     473             :                             ",deposit_fee_frac"
     474             :                             ",deposit_fee_curr"
     475             :                             ",refund_fee_val"
     476             :                             ",refund_fee_frac"
     477             :                             ",refund_fee_curr"
     478             :                             ",wire_fee_val"
     479             :                             ",wire_fee_frac"
     480             :                             ",wire_fee_curr"
     481             :                             ",exchange_proof"
     482             :                             " FROM merchant_deposits"
     483             :                             " WHERE h_contract_terms=$1"
     484             :                             " AND merchant_pub=$2",
     485             :                             2),
     486             :     GNUNET_PQ_make_prepare ("find_deposits_by_hash_and_coin",
     487             :                             "SELECT"
     488             :                             " amount_with_fee_val"
     489             :                             ",amount_with_fee_frac"
     490             :                             ",amount_with_fee_curr"
     491             :                             ",deposit_fee_val"
     492             :                             ",deposit_fee_frac"
     493             :                             ",deposit_fee_curr"
     494             :                             ",refund_fee_val"
     495             :                             ",refund_fee_frac"
     496             :                             ",refund_fee_curr"
     497             :                             ",wire_fee_val"
     498             :                             ",wire_fee_frac"
     499             :                             ",wire_fee_curr"
     500             :                             ",exchange_url"
     501             :                             ",exchange_proof"
     502             :                             " FROM merchant_deposits"
     503             :                             " WHERE h_contract_terms=$1"
     504             :                             " AND merchant_pub=$2"
     505             :                             " AND coin_pub=$3",
     506             :                             3),
     507             :     GNUNET_PQ_make_prepare ("find_transfers_by_hash",
     508             :                             "SELECT"
     509             :                             " coin_pub"
     510             :                             ",wtid"
     511             :                             ",merchant_proofs.execution_time"
     512             :                             ",merchant_proofs.proof"
     513             :                             " FROM merchant_transfers"
     514             :                             "   JOIN merchant_proofs USING (wtid)"
     515             :                             " WHERE h_contract_terms=$1",
     516             :                             1),
     517             :     GNUNET_PQ_make_prepare ("find_deposits_by_wtid",
     518             :                             "SELECT"
     519             :                             " merchant_transfers.h_contract_terms"
     520             :                             ",merchant_transfers.coin_pub"
     521             :                             ",merchant_deposits.amount_with_fee_val"
     522             :                             ",merchant_deposits.amount_with_fee_frac"
     523             :                             ",merchant_deposits.amount_with_fee_curr"
     524             :                             ",merchant_deposits.deposit_fee_val"
     525             :                             ",merchant_deposits.deposit_fee_frac"
     526             :                             ",merchant_deposits.deposit_fee_curr"
     527             :                             ",merchant_deposits.refund_fee_val"
     528             :                             ",merchant_deposits.refund_fee_frac"
     529             :                             ",merchant_deposits.refund_fee_curr"
     530             :                             ",merchant_deposits.wire_fee_val"
     531             :                             ",merchant_deposits.wire_fee_frac"
     532             :                             ",merchant_deposits.wire_fee_curr"
     533             :                             ",merchant_deposits.exchange_url"
     534             :                             ",merchant_deposits.exchange_proof"
     535             :                             " FROM merchant_transfers"
     536             :                             "   JOIN merchant_deposits"
     537             :                             "     ON (merchant_deposits.h_contract_terms = merchant_transfers.h_contract_terms"
     538             :                             "       AND"
     539             :                             "         merchant_deposits.coin_pub = merchant_transfers.coin_pub)"
     540             :                             " WHERE wtid=$1",
     541             :                             1),
     542             :     GNUNET_PQ_make_prepare ("find_proof_by_wtid",
     543             :                             "SELECT"
     544             :                             " proof"
     545             :                             " FROM merchant_proofs"
     546             :                             " WHERE wtid=$1"
     547             :                             "  AND exchange_url=$2",
     548             :                             2),
     549             :     GNUNET_PQ_make_prepare ("lookup_tip_reserve_balance",
     550             :                             "SELECT"
     551             :                             " expiration"
     552             :                             ",balance_val"
     553             :                             ",balance_frac"
     554             :                             ",balance_curr"
     555             :                             " FROM merchant_tip_reserves"
     556             :                             " WHERE reserve_priv=$1",
     557             :                             1),
     558             :     GNUNET_PQ_make_prepare ("find_tip_authorizations",
     559             :                             "SELECT"
     560             :                             " amount_val"
     561             :                             ",amount_frac"
     562             :                             ",amount_curr"
     563             :                             ",justification"
     564             :                             ",tip_id"
     565             :                             " FROM merchant_tips"
     566             :                             " WHERE reserve_priv=$1",
     567             :                             1),
     568             :     GNUNET_PQ_make_prepare ("update_tip_reserve_balance",
     569             :                             "UPDATE merchant_tip_reserves SET"
     570             :                             " expiration=$2"
     571             :                             ",balance_val=$3"
     572             :                             ",balance_frac=$4"
     573             :                             ",balance_curr=$5"
     574             :                             " WHERE reserve_priv=$1",
     575             :                             5),
     576             :     GNUNET_PQ_make_prepare ("insert_tip_reserve_balance",
     577             :                             "INSERT INTO merchant_tip_reserves"
     578             :                             "(reserve_priv"
     579             :                             ",expiration"
     580             :                             ",balance_val"
     581             :                             ",balance_frac"
     582             :                             ",balance_curr"
     583             :                             ") VALUES "
     584             :                             "($1, $2, $3, $4, $5)",
     585             :                             5),
     586             :     GNUNET_PQ_make_prepare ("insert_tip_justification",
     587             :                             "INSERT INTO merchant_tips"
     588             :                             "(reserve_priv"
     589             :                             ",tip_id"
     590             :                             ",exchange_url"
     591             :                             ",justification"
     592             :                             ",timestamp"
     593             :                             ",amount_val"
     594             :                             ",amount_frac"
     595             :                             ",amount_curr"
     596             :                             ",left_val"
     597             :                             ",left_frac"
     598             :                             ",left_curr"
     599             :                             ") VALUES "
     600             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
     601             :                             11),
     602             :     GNUNET_PQ_make_prepare ("lookup_reserve_by_tip_id",
     603             :                             "SELECT"
     604             :                             " reserve_priv"
     605             :                             ",left_val"
     606             :                             ",left_frac"
     607             :                             ",left_curr"
     608             :                             " FROM merchant_tips"
     609             :                             " WHERE tip_id=$1",
     610             :                             1),
     611             :     GNUNET_PQ_make_prepare ("lookup_amount_by_pickup",
     612             :                             "SELECT"
     613             :                             " amount_val"
     614             :                             ",amount_frac"
     615             :                             ",amount_curr"
     616             :                             " FROM merchant_tip_pickups"
     617             :                             " WHERE pickup_id=$1"
     618             :                             " AND tip_id=$2",
     619             :                             2),
     620             :     GNUNET_PQ_make_prepare ("find_tip_by_id",
     621             :                             "SELECT"
     622             :                             " exchange_url"
     623             :                             ",timestamp"
     624             :                             ",amount_val"
     625             :                             ",amount_frac"
     626             :                             ",amount_curr"
     627             :                             " FROM merchant_tips"
     628             :                             " WHERE tip_id=$1",
     629             :                             1),
     630             :     GNUNET_PQ_make_prepare ("update_tip_balance",
     631             :                             "UPDATE merchant_tips SET"
     632             :                             " left_val=$2"
     633             :                             ",left_frac=$3"
     634             :                             ",left_curr=$4"
     635             :                             " WHERE tip_id=$1",
     636             :                             4),
     637             :     GNUNET_PQ_make_prepare ("insert_pickup_id",
     638             :                             "INSERT INTO merchant_tip_pickups"
     639             :                             "(tip_id"
     640             :                             ",pickup_id"
     641             :                             ",amount_val"
     642             :                             ",amount_frac"
     643             :                             ",amount_curr"
     644             :                             ") VALUES "
     645             :                             "($1, $2, $3, $4, $5)",
     646             :                             5),
     647             :     GNUNET_PQ_make_prepare ("insert_tip_credit_uuid",
     648             :                             "INSERT INTO merchant_tip_reserve_credits"
     649             :                             "(reserve_priv"
     650             :                             ",credit_uuid"
     651             :                             ",timestamp"
     652             :                             ",amount_val"
     653             :                             ",amount_frac"
     654             :                             ",amount_curr)"
     655             :                             " VALUES "
     656             :                             "($1, $2, $3, $4, $5, $6)",
     657             :                             6),
     658             :     GNUNET_PQ_make_prepare ("lookup_tip_credit_uuid",
     659             :                             "SELECT 1 "
     660             :                             "FROM merchant_tip_reserve_credits "
     661             :                             "WHERE credit_uuid=$1 AND reserve_priv=$2",
     662             :                             2),
     663             :     GNUNET_PQ_PREPARED_STATEMENT_END
     664             :   };
     665             : 
     666           5 :   if (GNUNET_OK !=
     667           5 :       GNUNET_PQ_exec_statements (pg->conn,
     668             :                                  es))
     669             :   {
     670           0 :     GNUNET_break (0);
     671           0 :     return GNUNET_SYSERR;
     672             :   }
     673             : 
     674           5 :   if (GNUNET_OK !=
     675           5 :       GNUNET_PQ_prepare_statements (pg->conn,
     676             :                                     ps))
     677             :   {
     678           0 :     GNUNET_break (0);
     679           0 :     return GNUNET_SYSERR;
     680             :   }
     681           5 :   return GNUNET_OK;
     682             : }
     683             : 
     684             : 
     685             : /**
     686             :  * Check that the database connection is still up.
     687             :  *
     688             :  * @param pg connection to check
     689             :  */
     690             : static void
     691         590 : check_connection (struct PostgresClosure *pg)
     692             : {
     693         590 :   if (CONNECTION_BAD != PQstatus (pg->conn))
     694         590 :     return;
     695           0 :   PQfinish (pg->conn);
     696           0 :   pg->conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
     697             :                                          "merchantdb-postgres");
     698           0 :   GNUNET_break (NULL != pg->conn);
     699           0 :   GNUNET_break (GNUNET_OK ==
     700             :                 postgres_initialize (pg));
     701             : }
     702             : 
     703             : 
     704             : /**
     705             :  * Start a transaction.
     706             :  *
     707             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     708             :  * @param name unique name identifying the transaction (for debugging),
     709             :  *             must point to a constant
     710             :  * @return #GNUNET_OK on success
     711             :  */
     712             : static int
     713         124 : postgres_start (void *cls,
     714             :                 const char *name)
     715             : {
     716         124 :   struct PostgresClosure *pg = cls;
     717             :   PGresult *result;
     718             :   ExecStatusType ex;
     719             : 
     720         124 :   check_connection (pg);
     721         124 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     722             :               "Starting merchant DB transaction\n");
     723         124 :   result = PQexec (pg->conn,
     724             :                    "START TRANSACTION ISOLATION LEVEL SERIALIZABLE");
     725         124 :   if (PGRES_COMMAND_OK !=
     726             :       (ex = PQresultStatus (result)))
     727             :   {
     728           0 :     TALER_LOG_ERROR ("Failed to start transaction (%s): %s\n",
     729             :                      PQresStatus (ex),
     730             :                      PQerrorMessage (pg->conn));
     731           0 :     GNUNET_break (0);
     732           0 :     PQclear (result);
     733           0 :     return GNUNET_SYSERR;
     734             :   }
     735         124 :   PQclear (result);
     736         124 :   pg->transaction_name = name;
     737         124 :   return GNUNET_OK;
     738             : }
     739             : 
     740             : 
     741             : /**
     742             :  * Roll back the current transaction of a database connection.
     743             :  *
     744             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     745             :  * @return #GNUNET_OK on success
     746             :  */
     747             : static void
     748          69 : postgres_rollback (void *cls)
     749             : {
     750          69 :   struct PostgresClosure *pg = cls;
     751             :   PGresult *result;
     752             : 
     753          69 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     754             :               "Rolling back merchant DB transaction\n");
     755          69 :   result = PQexec (pg->conn,
     756             :                    "ROLLBACK");
     757          69 :   GNUNET_break (PGRES_COMMAND_OK ==
     758             :                 PQresultStatus (result));
     759          69 :   PQclear (result);
     760          69 :   pg->transaction_name = NULL;
     761          69 : }
     762             : 
     763             : 
     764             : /**
     765             :  * Do a pre-flight check that we are not in an uncommitted transaction.
     766             :  * If we are, try to commit the previous transaction and output a warning.
     767             :  * Does not return anything, as we will continue regardless of the outcome.
     768             :  *
     769             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     770             :  */
     771             : static void
     772         152 : postgres_preflight (void *cls)
     773             : {
     774         152 :   struct PostgresClosure *pg = cls;
     775             :   PGresult *result;
     776             :   ExecStatusType status;
     777             : 
     778         152 :   if (NULL == pg->transaction_name)
     779         149 :     return; /* all good */
     780           3 :   result = PQexec (pg->conn,
     781             :                    "COMMIT");
     782           3 :   status = PQresultStatus (result);
     783           3 :   if (PGRES_COMMAND_OK == status)
     784             :   {
     785           3 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     786             :                 "BUG: Preflight check committed transaction `%s'!\n",
     787             :                 pg->transaction_name);
     788             :   }
     789             :   else
     790             :   {
     791           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     792             :                 "BUG: Preflight check failed to commit transaction `%s'!\n",
     793             :                 pg->transaction_name);
     794             :   }
     795           3 :   pg->transaction_name = NULL;
     796           3 :   PQclear (result);
     797             : }
     798             : 
     799             : 
     800             : /**
     801             :  * Commit the current transaction of a database connection.
     802             :  *
     803             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     804             :  * @return transaction status code
     805             :  */
     806             : static enum GNUNET_DB_QueryStatus
     807          61 : postgres_commit (void *cls)
     808             : {
     809          61 :   struct PostgresClosure *pg = cls;
     810          61 :   struct GNUNET_PQ_QueryParam params[] = {
     811             :     GNUNET_PQ_query_param_end
     812             :   };
     813             : 
     814          61 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     815             :               "Committing merchant DB transaction\n");
     816          61 :   pg->transaction_name = NULL;
     817          61 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
     818             :                                              "end_transaction",
     819             :                                              params);
     820             : }
     821             : 
     822             : 
     823             : /**
     824             :  * Retrieve proposal data given its proposal data's hashcode
     825             :  *
     826             :  * @param cls closure
     827             :  * @param contract_terms where to store the retrieved proposal data
     828             :  * @param h_contract_terms proposal data's hashcode that will be used to
     829             :  * perform the lookup
     830             :  * @return transaction status
     831             :  */
     832             : static enum GNUNET_DB_QueryStatus
     833          13 : postgres_find_contract_terms_from_hash (void *cls,
     834             :                                         json_t **contract_terms,
     835             :                                         const struct GNUNET_HashCode *h_contract_terms,
     836             :                                         const struct TALER_MerchantPublicKeyP *merchant_pub)
     837             : {
     838          13 :   struct PostgresClosure *pg = cls;
     839          13 :   struct GNUNET_PQ_QueryParam params[] = {
     840             :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
     841             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
     842             :     GNUNET_PQ_query_param_end
     843             :   };
     844          13 :   struct GNUNET_PQ_ResultSpec rs[] = {
     845             :     TALER_PQ_result_spec_json ("contract_terms",
     846             :                                contract_terms),
     847             :     GNUNET_PQ_result_spec_end
     848             :   };
     849             : 
     850          13 :   check_connection (pg);
     851          13 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
     852             :                                                    "find_contract_terms_from_hash",
     853             :                                                    params,
     854             :                                                    rs);
     855             : }
     856             : 
     857             : 
     858             : /**
     859             :  * Retrieve proposal data given its proposal data's hashcode
     860             :  *
     861             :  * @param cls closure
     862             :  * @param contract_terms where to store the retrieved proposal data
     863             :  * @param h_contract_terms proposal data's hashcode that will be used to
     864             :  * perform the lookup
     865             :  * @return transaction status
     866             :  */
     867             : static enum GNUNET_DB_QueryStatus
     868          14 : postgres_find_paid_contract_terms_from_hash (void *cls,
     869             :                                              json_t **contract_terms,
     870             :                                              const struct GNUNET_HashCode *h_contract_terms,
     871             :                                              const struct TALER_MerchantPublicKeyP *merchant_pub)
     872             : {
     873          14 :   struct PostgresClosure *pg = cls;
     874          14 :   struct GNUNET_PQ_QueryParam params[] = {
     875             :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
     876             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
     877             :     GNUNET_PQ_query_param_end
     878             :   };
     879          14 :   struct GNUNET_PQ_ResultSpec rs[] = {
     880             :     TALER_PQ_result_spec_json ("contract_terms",
     881             :                                contract_terms),
     882             :     GNUNET_PQ_result_spec_end
     883             :   };
     884             : 
     885          14 :   check_connection (pg);
     886          14 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
     887             :                                                    "find_paid_contract_terms_from_hash",
     888             :                                                    params,
     889             :                                                    rs);
     890             : }
     891             : 
     892             : 
     893             : /**
     894             :  * Retrieve proposal data given its order id.  Ignores if the
     895             :  * proposal has been paid or not.
     896             :  *
     897             :  * @param cls closure
     898             :  * @param[out] contract_terms where to store the retrieved contract terms
     899             :  * @param[out] last_session_id where to store the result
     900             :  * @param order id order id used to perform the lookup
     901             :  * @return transaction status
     902             :  */
     903             : static enum GNUNET_DB_QueryStatus
     904          78 : postgres_find_contract_terms (void *cls,
     905             :                               json_t **contract_terms,
     906             :                               char **last_session_id,
     907             :                               const char *order_id,
     908             :                               const struct TALER_MerchantPublicKeyP *merchant_pub)
     909             : {
     910          78 :   struct PostgresClosure *pg = cls;
     911             : 
     912          78 :   struct GNUNET_PQ_QueryParam params[] = {
     913             :     GNUNET_PQ_query_param_string (order_id),
     914             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
     915             :     GNUNET_PQ_query_param_end
     916             :   };
     917          78 :   struct GNUNET_PQ_ResultSpec rs[] = {
     918             :     TALER_PQ_result_spec_json ("contract_terms",
     919             :                                contract_terms),
     920             :     GNUNET_PQ_result_spec_string ("last_session_id",
     921             :                                   last_session_id),
     922             :     GNUNET_PQ_result_spec_end
     923             :   };
     924             : 
     925          78 :   *contract_terms = NULL;
     926          78 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     927             :               "Finding contract term, order_id: '%s', merchant_pub: '%s'.\n",
     928             :               order_id,
     929             :               TALER_B2S (merchant_pub));
     930          78 :   check_connection (pg);
     931          78 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
     932             :                                                    "find_contract_terms",
     933             :                                                    params,
     934             :                                                    rs);
     935             : }
     936             : 
     937             : 
     938             : /**
     939             :  * Retrieve order given its order id and the instance's merchant public key.
     940             :  *
     941             :  * @param cls closure
     942             :  * @param[out] contract_terms where to store the retrieved contract terms
     943             :  * @param order id order id used to perform the lookup
     944             :  * @param merchant_pub merchant public key that identifies the instance
     945             :  * @return transaction status
     946             :  */
     947             : static enum GNUNET_DB_QueryStatus
     948          34 : postgres_find_order (void *cls,
     949             :                      json_t **contract_terms,
     950             :                      const char *order_id,
     951             :                      const struct TALER_MerchantPublicKeyP *merchant_pub)
     952             : {
     953          34 :   struct PostgresClosure *pg = cls;
     954             : 
     955          34 :   struct GNUNET_PQ_QueryParam params[] = {
     956             :     GNUNET_PQ_query_param_string (order_id),
     957             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
     958             :     GNUNET_PQ_query_param_end
     959             :   };
     960          34 :   struct GNUNET_PQ_ResultSpec rs[] = {
     961             :     TALER_PQ_result_spec_json ("contract_terms",
     962             :                                contract_terms),
     963             :     GNUNET_PQ_result_spec_end
     964             :   };
     965             : 
     966          34 :   *contract_terms = NULL;
     967          34 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     968             :               "Finding contract term, order_id: '%s', merchant_pub: '%s'.\n",
     969             :               order_id,
     970             :               TALER_B2S (merchant_pub));
     971          34 :   check_connection (pg);
     972          34 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
     973             :                                                    "find_order",
     974             :                                                    params,
     975             :                                                    rs);
     976             : }
     977             : 
     978             : 
     979             : /**
     980             :  * Insert proposal data and its hashcode into db
     981             :  *
     982             :  * @param cls closure
     983             :  * @param order_id identificator of the proposal being stored
     984             :  * @param merchant_pub merchant's public key
     985             :  * @param timestamp timestamp of this proposal data
     986             :  * @param contract_terms proposal data to store
     987             :  * @return transaction status
     988             :  */
     989             : static enum GNUNET_DB_QueryStatus
     990          19 : postgres_insert_contract_terms (void *cls,
     991             :                                const char *order_id,
     992             :                                const struct TALER_MerchantPublicKeyP *merchant_pub,
     993             :                                struct GNUNET_TIME_Absolute timestamp,
     994             :                                const json_t *contract_terms)
     995             : {
     996          19 :   struct PostgresClosure *pg = cls;
     997             :   struct GNUNET_HashCode h_contract_terms;
     998          19 :   struct GNUNET_PQ_QueryParam params[] = {
     999             :     GNUNET_PQ_query_param_string (order_id),
    1000             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    1001             :     GNUNET_PQ_query_param_absolute_time (&timestamp),
    1002             :     TALER_PQ_query_param_json (contract_terms),
    1003             :     GNUNET_PQ_query_param_auto_from_type (&h_contract_terms),
    1004             :     GNUNET_PQ_query_param_end
    1005             :   };
    1006             : 
    1007          19 :   if (GNUNET_OK !=
    1008          19 :       TALER_JSON_hash (contract_terms,
    1009             :                        &h_contract_terms))
    1010             :   {
    1011           0 :     GNUNET_break (0);
    1012           0 :     return GNUNET_SYSERR;
    1013             :   }
    1014          19 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1015             :               "inserting contract_terms: order_id: %s, merchant_pub: %s, h_contract_terms: %s.\n",
    1016             :               order_id,
    1017             :               TALER_B2S (merchant_pub),
    1018             :               GNUNET_h2s (&h_contract_terms));
    1019          19 :   check_connection (pg);
    1020          19 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1021             :                                              "insert_contract_terms",
    1022             :                                              params);
    1023             : }
    1024             : 
    1025             : 
    1026             : /**
    1027             :  * Insert order into the DB.
    1028             :  *
    1029             :  * @param cls closure
    1030             :  * @param order_id identificator of the proposal being stored
    1031             :  * @param merchant_pub merchant's public key
    1032             :  * @param timestamp timestamp of this proposal data
    1033             :  * @param contract_terms proposal data to store
    1034             :  * @return transaction status
    1035             :  */
    1036             : static enum GNUNET_DB_QueryStatus
    1037          17 : postgres_insert_order (void *cls,
    1038             :                        const char *order_id,
    1039             :                        const struct TALER_MerchantPublicKeyP *merchant_pub,
    1040             :                        struct GNUNET_TIME_Absolute timestamp,
    1041             :                        const json_t *contract_terms)
    1042             : {
    1043          17 :   struct PostgresClosure *pg = cls;
    1044          17 :   struct GNUNET_PQ_QueryParam params[] = {
    1045             :     GNUNET_PQ_query_param_string (order_id),
    1046             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    1047             :     GNUNET_PQ_query_param_absolute_time (&timestamp),
    1048             :     TALER_PQ_query_param_json (contract_terms),
    1049             :     GNUNET_PQ_query_param_end
    1050             :   };
    1051             : 
    1052          17 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1053             :               "inserting order: order_id: %s, merchant_pub: %s.\n",
    1054             :               order_id,
    1055             :               TALER_B2S (merchant_pub));
    1056          17 :   check_connection (pg);
    1057          17 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1058             :                                              "insert_order",
    1059             :                                              params);
    1060             : }
    1061             : 
    1062             : 
    1063             : /**
    1064             :  * Mark contract terms as payed.  Needed by /history as only payed
    1065             :  * contracts must be shown.
    1066             :  *
    1067             :  * NOTE: we can't get the list of (payed) contracts from the
    1068             :  * transactions table because it lacks contract_terms plain JSON.  In
    1069             :  * facts, the protocol doesn't allow to store contract_terms in
    1070             :  * transactions table, as /pay handler doesn't receive this data (only
    1071             :  * /proposal does).
    1072             :  *
    1073             :  * @param cls closure
    1074             :  * @param h_contract_terms hash of the contract that is now paid
    1075             :  * @param merchant_pub merchant's public key
    1076             :  * @param last_session_id session id used for the payment, NULL
    1077             :  *        if payment was not session-bound
    1078             :  * @return transaction status
    1079             :  */
    1080             : static enum GNUNET_DB_QueryStatus
    1081          19 : postgres_mark_proposal_paid (void *cls,
    1082             :                              const struct GNUNET_HashCode *h_contract_terms,
    1083             :                              const struct TALER_MerchantPublicKeyP *merchant_pub,
    1084             :                              const char *last_session_id)
    1085             : {
    1086          19 :   struct PostgresClosure *pg = cls;
    1087          38 :   struct GNUNET_PQ_QueryParam params[] = {
    1088             :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    1089             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    1090          19 :     GNUNET_PQ_query_param_string ((last_session_id == NULL) ? "" : last_session_id),
    1091             :     GNUNET_PQ_query_param_end
    1092             :   };
    1093             : 
    1094          19 :   TALER_LOG_DEBUG ("Marking proposal paid, h_contract_terms: '%s',"
    1095             :                    " merchant_pub: '%s'\n",
    1096             :                    GNUNET_h2s (h_contract_terms),
    1097             :                    TALER_B2S (merchant_pub));
    1098          19 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1099             :                                              "mark_proposal_paid",
    1100             :                                              params);
    1101             : }
    1102             : 
    1103             : 
    1104             : /**
    1105             :  * Insert payment confirmation from the exchange into the database.
    1106             :  *
    1107             :  * @param cls closure
    1108             :  * @param order_id identificator of the proposal associated with this revenue
    1109             :  * @param merchant_pub merchant's public key
    1110             :  * @param coin_pub public key of the coin
    1111             :  * @param amount_with_fee amount the exchange will deposit for this coin
    1112             :  * @param deposit_fee fee the exchange will charge for this coin
    1113             :  * @param refund_fee fee the exchange will charge for refunding this coin
    1114             :  * @param wire_fee wire fee changed by the exchange
    1115             :  * @param signkey_pub public key used by the exchange for @a exchange_proof
    1116             :  * @param exchange_proof proof from exchange that coin was accepted
    1117             :  * @return transaction status
    1118             :  */
    1119             : static enum GNUNET_DB_QueryStatus
    1120          20 : postgres_store_deposit (void *cls,
    1121             :                         const struct GNUNET_HashCode *h_contract_terms,
    1122             :                         const struct TALER_MerchantPublicKeyP *merchant_pub,
    1123             :                         const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1124             :                         const char *exchange_url,
    1125             :                         const struct TALER_Amount *amount_with_fee,
    1126             :                         const struct TALER_Amount *deposit_fee,
    1127             :                         const struct TALER_Amount *refund_fee,
    1128             :                         const struct TALER_Amount *wire_fee,
    1129             :                         const struct TALER_ExchangePublicKeyP *signkey_pub,
    1130             :                         const json_t *exchange_proof)
    1131             : {
    1132          20 :   struct PostgresClosure *pg = cls;
    1133          20 :   struct GNUNET_PQ_QueryParam params[] = {
    1134             :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    1135             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    1136             :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    1137             :     GNUNET_PQ_query_param_string (exchange_url),
    1138             :     TALER_PQ_query_param_amount (amount_with_fee),
    1139             :     TALER_PQ_query_param_amount (deposit_fee),
    1140             :     TALER_PQ_query_param_amount (refund_fee),
    1141             :     TALER_PQ_query_param_amount (wire_fee),
    1142             :     GNUNET_PQ_query_param_auto_from_type (signkey_pub),
    1143             :     TALER_PQ_query_param_json (exchange_proof),
    1144             :     GNUNET_PQ_query_param_end
    1145             :   };
    1146             : 
    1147          20 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1148             :               "Storing payment for h_contract_terms `%s', coin_pub: `%s', amount_with_fee: %s\n",
    1149             :               GNUNET_h2s (h_contract_terms),
    1150             :               TALER_B2S (coin_pub),
    1151             :               TALER_amount2s (amount_with_fee));
    1152          20 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1153             :               "Merchant pub is `%s'\n",
    1154             :               TALER_B2S (merchant_pub));
    1155          20 :   check_connection (pg);
    1156          20 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1157             :                                              "insert_deposit",
    1158             :                                              params);
    1159             : }
    1160             : 
    1161             : 
    1162             : /**
    1163             :  * Insert mapping of @a coin_pub and @a h_contract_terms to
    1164             :  * corresponding @a wtid.
    1165             :  *
    1166             :  * @param cls closure
    1167             :  * @param h_contract_terms hashcode of the proposal data paid by @a coin_pub
    1168             :  * @param coin_pub public key of the coin
    1169             :  * @param wtid identifier of the wire transfer in which the exchange
    1170             :  *             send us the money for the coin deposit
    1171             :  * @return transaction status
    1172             :  */
    1173             : static enum GNUNET_DB_QueryStatus
    1174           7 : postgres_store_coin_to_transfer (void *cls,
    1175             :                                  const struct GNUNET_HashCode *h_contract_terms,
    1176             :                                  const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1177             :                                  const struct TALER_WireTransferIdentifierRawP *wtid)
    1178             : {
    1179           7 :   struct PostgresClosure *pg = cls;
    1180           7 :   struct GNUNET_PQ_QueryParam params[] = {
    1181             :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    1182             :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    1183             :     GNUNET_PQ_query_param_auto_from_type (wtid),
    1184             :     GNUNET_PQ_query_param_end
    1185             :   };
    1186             : 
    1187           7 :   check_connection (pg);
    1188           7 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1189             :                                              "insert_transfer",
    1190             :                                              params);
    1191             : }
    1192             : 
    1193             : 
    1194             : /**
    1195             :  * Insert wire transfer confirmation from the exchange into the database.
    1196             :  *
    1197             :  * @param cls closure
    1198             :  * @param exchange_url URL of the exchange
    1199             :  * @param wtid identifier of the wire transfer
    1200             :  * @param execution_time when was @a wtid executed
    1201             :  * @param signkey_pub public key used by the exchange for @a exchange_proof
    1202             :  * @param exchange_proof proof from exchange about what the deposit was for
    1203             :  * @return transaction status
    1204             :  */
    1205             : static enum GNUNET_DB_QueryStatus
    1206           7 : postgres_store_transfer_to_proof (void *cls,
    1207             :                                   const char *exchange_url,
    1208             :                                   const struct TALER_WireTransferIdentifierRawP *wtid,
    1209             :                                   struct GNUNET_TIME_Absolute execution_time,
    1210             :                                   const struct TALER_ExchangePublicKeyP *signkey_pub,
    1211             :                                   const json_t *exchange_proof)
    1212             : {
    1213           7 :   struct PostgresClosure *pg = cls;
    1214           7 :   struct GNUNET_PQ_QueryParam params[] = {
    1215             :     GNUNET_PQ_query_param_string (exchange_url),
    1216             :     GNUNET_PQ_query_param_auto_from_type (wtid),
    1217             :     GNUNET_PQ_query_param_absolute_time (&execution_time),
    1218             :     GNUNET_PQ_query_param_auto_from_type (signkey_pub),
    1219             :     TALER_PQ_query_param_json (exchange_proof),
    1220             :     GNUNET_PQ_query_param_end
    1221             :   };
    1222             : 
    1223           7 :   check_connection (pg);
    1224           7 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1225             :                                              "insert_proof",
    1226             :                                              params);
    1227             : }
    1228             : 
    1229             : 
    1230             : /**
    1231             :  * Lookup for a proposal, respecting the signature used by the
    1232             :  * /history's db methods.
    1233             :  *
    1234             :  * @param cls db plugin handle
    1235             :  * @param order_id order id used to search for the proposal data
    1236             :  * @param merchant_pub public key of the merchant using this method
    1237             :  * @param cb the callback
    1238             :  * @param cb_cls closure to pass to the callback
    1239             :  * @return transaction status
    1240             :  */
    1241             : static enum GNUNET_DB_QueryStatus
    1242           1 : postgres_find_contract_terms_history (void *cls,
    1243             :                                       const char *order_id,
    1244             :                                       const struct TALER_MerchantPublicKeyP *merchant_pub,
    1245             :                                       TALER_MERCHANTDB_ProposalDataCallback cb,
    1246             :                                       void *cb_cls)
    1247             : {
    1248           1 :   struct PostgresClosure *pg = cls;
    1249             :   json_t *contract_terms;
    1250             :   enum GNUNET_DB_QueryStatus qs;
    1251           1 :   struct GNUNET_PQ_QueryParam params[] = {
    1252             :     GNUNET_PQ_query_param_string (order_id),
    1253             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    1254             :     GNUNET_PQ_query_param_end
    1255             :   };
    1256           1 :   struct GNUNET_PQ_ResultSpec rs[] = {
    1257             :     TALER_PQ_result_spec_json ("contract_terms",
    1258             :                                &contract_terms),
    1259             :     GNUNET_PQ_result_spec_end
    1260             :   };
    1261             : 
    1262           1 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    1263             :                                                  "find_contract_terms_history",
    1264             :                                                  params,
    1265             :                                                  rs);
    1266           1 :   if (qs <= 0)
    1267           0 :     return qs;
    1268           1 :   if (NULL != cb)
    1269           1 :     cb (cb_cls,
    1270             :         order_id,
    1271             :         0,
    1272             :         contract_terms);
    1273           1 :   GNUNET_PQ_cleanup_result (rs);
    1274           1 :   return qs;
    1275             : }
    1276             : 
    1277             : 
    1278             : /**
    1279             :  * Closure for #find_contracts_cb().
    1280             :  */
    1281             : struct FindContractsContext
    1282             : {
    1283             :   /**
    1284             :    * Function to call on each result.
    1285             :    */
    1286             :   TALER_MERCHANTDB_ProposalDataCallback cb;
    1287             : 
    1288             :   /**
    1289             :    * Closure for @e cb.
    1290             :    */
    1291             :   void *cb_cls;
    1292             : 
    1293             :   /**
    1294             :    * Transaction status code to set.
    1295             :    */
    1296             :   enum GNUNET_DB_QueryStatus qs;
    1297             : };
    1298             : 
    1299             : 
    1300             : /**
    1301             :  * Function to be called with the results of a SELECT statement
    1302             :  * that has returned @a num_results results.
    1303             :  *
    1304             :  * @param cls of type `struct FindContractsContext *`
    1305             :  * @param result the postgres result
    1306             :  * @param num_result the number of results in @a result
    1307             :  */
    1308             : static void
    1309          12 : find_contracts_cb (void *cls,
    1310             :                    PGresult *result,
    1311             :                    unsigned int num_results)
    1312             : {
    1313          12 :   struct FindContractsContext *fcctx = cls;
    1314             : 
    1315          24 :   for (unsigned int i = 0; i < num_results; i++)
    1316             :   {
    1317             :     char *order_id;
    1318             :     json_t *contract_terms;
    1319             :     uint64_t row_id;
    1320          12 :     struct GNUNET_PQ_ResultSpec rs[] = {
    1321             :       GNUNET_PQ_result_spec_string ("order_id",
    1322             :                                     &order_id),
    1323             :       TALER_PQ_result_spec_json ("contract_terms",
    1324             :                                  &contract_terms),
    1325             :       GNUNET_PQ_result_spec_uint64 ("row_id",
    1326             :                                     &row_id),
    1327             :       GNUNET_PQ_result_spec_end
    1328             :     };
    1329             : 
    1330          12 :     if (GNUNET_OK !=
    1331          12 :         GNUNET_PQ_extract_result (result,
    1332             :                                   rs,
    1333             :                                   i))
    1334             :     {
    1335           0 :       GNUNET_break (0);
    1336           0 :       fcctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1337           0 :       return;
    1338             :     }
    1339          12 :     fcctx->qs = i + 1;
    1340          12 :     fcctx->cb (fcctx->cb_cls,
    1341             :                order_id,
    1342             :                row_id,
    1343             :                contract_terms);
    1344          12 :     GNUNET_PQ_cleanup_result (rs);
    1345             :   }
    1346             : }
    1347             : 
    1348             : 
    1349             : /**
    1350             :  * Return proposals whose timestamp are older than `date`.
    1351             :  * Among those proposals, only those ones being between the
    1352             :  * start-th and (start-nrows)-th record are returned.  The rows
    1353             :  * are sorted having the youngest first.
    1354             :  *
    1355             :  * @param cls our plugin handle.
    1356             :  * @param date only results older than this date are returned.
    1357             :  * @param merchant_pub instance's public key; only rows related to this
    1358             :  * instance are returned.
    1359             :  * @param start only rows with serial id less than start are returned.
    1360             :  * In other words, you lower `start` to get older records. The tipical
    1361             :  * usage is to firstly call `find_contract_terms_by_date`, so that you get
    1362             :  * the `nrows` youngest records. The oldest of those records will tell you
    1363             :  * from which timestamp and `start` you can query the DB in order to get
    1364             :  * furtherly older records, and so on. Alternatively, you can use always
    1365             :  * the same timestamp and just go behind in history by tuning `start`.
    1366             :  * @param nrows only nrows rows are returned.
    1367             :  * @param future if set to #GNUNET_YES, retrieves rows younger than `date`.
    1368             :  * This is tipically used to show live updates on the merchant's backoffice
    1369             :  * Web interface.
    1370             :  * @param cb function to call with transaction data, can be NULL.
    1371             :  * @param cb_cls closure for @a cb
    1372             :  * @return transaction status
    1373             :  */
    1374             : static enum GNUNET_DB_QueryStatus
    1375          11 : postgres_find_contract_terms_by_date_and_range (void *cls,
    1376             :                                                 struct GNUNET_TIME_Absolute date,
    1377             :                                                 const struct TALER_MerchantPublicKeyP *merchant_pub,
    1378             :                                                 uint64_t start,
    1379             :                                                 uint64_t nrows,
    1380             :                                                 int future,
    1381             :                                                 TALER_MERCHANTDB_ProposalDataCallback cb,
    1382             :                                                 void *cb_cls)
    1383             : {
    1384          11 :   struct PostgresClosure *pg = cls;
    1385          11 :   struct GNUNET_PQ_QueryParam params[] = {
    1386             :     GNUNET_PQ_query_param_absolute_time (&date),
    1387             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    1388             :     GNUNET_PQ_query_param_uint64 (&start),
    1389             :     GNUNET_PQ_query_param_uint64 (&nrows),
    1390             :     GNUNET_PQ_query_param_end
    1391             :   };
    1392             :   const char *stmt;
    1393             :   enum GNUNET_DB_QueryStatus qs;
    1394          11 :   struct FindContractsContext fcctx = {
    1395             :     .cb = cb,
    1396             :     .cb_cls = cb_cls
    1397             :   };
    1398          11 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1399             :               "DB serving /history with date %s\n",
    1400             :               GNUNET_STRINGS_absolute_time_to_string (date));
    1401          11 :   if (GNUNET_YES == future)
    1402           1 :     stmt = "find_contract_terms_by_date_and_range_future";
    1403             :   else
    1404          10 :     stmt = "find_contract_terms_by_date_and_range";
    1405          11 :   check_connection (pg);
    1406          11 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    1407             :                                              stmt,
    1408             :                                              params,
    1409             :                                              &find_contracts_cb,
    1410             :                                              &fcctx);
    1411          11 :   if (0 >= qs)
    1412           3 :     return qs;
    1413           8 :   return fcctx.qs;
    1414             : }
    1415             : 
    1416             : 
    1417             : /**
    1418             :  * Closure for #find_tip_authorizations_cb().
    1419             :  */
    1420             : struct GetAuthorizedTipAmountContext
    1421             : {
    1422             :   /**
    1423             :    * Total authorized amount.
    1424             :    */
    1425             :   struct TALER_Amount authorized_amount;
    1426             : 
    1427             :   /**
    1428             :    * Transaction status code to set.
    1429             :    */
    1430             :   enum GNUNET_DB_QueryStatus qs;
    1431             : 
    1432             : };
    1433             : 
    1434             : 
    1435             : 
    1436             : 
    1437             : /**
    1438             :  * Function to be called with the results of a SELECT statement
    1439             :  * that has returned @a num_results results.
    1440             :  *
    1441             :  * @param cls of type `struct GetAuthorizedTipAmountContext *`
    1442             :  * @param result the postgres result
    1443             :  * @param num_result the number of results in @a result
    1444             :  */
    1445             : static void
    1446           8 : find_tip_authorizations_cb (void *cls,
    1447             :                             PGresult *result,
    1448             :                             unsigned int num_results)
    1449             : {
    1450           8 :   struct GetAuthorizedTipAmountContext *ctx = cls;
    1451             :   unsigned int i;
    1452             : 
    1453          48 :   for (i = 0; i < num_results; i++)
    1454             :   {
    1455             :     struct TALER_Amount amount;
    1456             :     char *just;
    1457             :     struct GNUNET_HashCode h;
    1458          16 :     struct GNUNET_PQ_ResultSpec rs[] = {
    1459             :       GNUNET_PQ_result_spec_string ("justification", &just),
    1460             :       GNUNET_PQ_result_spec_auto_from_type ("tip_id", &h),
    1461             :       TALER_PQ_result_spec_amount ("amount",
    1462             :                                     &amount),
    1463             :       GNUNET_PQ_result_spec_end
    1464             :     };
    1465             : 
    1466          16 :     if (GNUNET_OK !=
    1467          16 :         GNUNET_PQ_extract_result (result,
    1468             :                                   rs,
    1469             :                                   i))
    1470             :     {
    1471           0 :       GNUNET_break (0);
    1472           0 :       ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1473           0 :       return;
    1474             :     }
    1475             : 
    1476          16 :     if (0 == i)
    1477             :     {
    1478           8 :       memcpy (&ctx->authorized_amount, &amount, sizeof (struct TALER_Amount));
    1479             :     }
    1480           8 :     else if (GNUNET_OK !=
    1481           8 :              TALER_amount_add (&ctx->authorized_amount,
    1482           8 :                                &ctx->authorized_amount, &amount))
    1483             :     {
    1484           0 :       GNUNET_break (0);
    1485           0 :       ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1486           0 :       return;
    1487             :     }
    1488             :   }
    1489             : 
    1490           8 :   if (0 == i)
    1491             :   {
    1492           0 :     ctx->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    1493             :   }
    1494             :   else
    1495             :   {
    1496             :     /* one aggregated result */
    1497           8 :     ctx->qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1498             :   }
    1499             : }
    1500             : 
    1501             : 
    1502             : /**
    1503             :  * Get the total amount of authorized tips for a tipping reserve.
    1504             :  *
    1505             :  * @param cls closure, typically a connection to the db
    1506             :  * @param reserve_priv which reserve to check
    1507             :  * @param[out] authorzed_amount amount we've authorized so far for tips
    1508             :  * @return transaction status, usually
    1509             :  *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
    1510             :  *      #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the reserve_priv
    1511             :  *      does not identify a known tipping reserve
    1512             :  */
    1513             : static enum GNUNET_DB_QueryStatus
    1514           8 : postgres_get_authorized_tip_amount (void *cls,
    1515             :                                     const struct TALER_ReservePrivateKeyP *reserve_priv,
    1516             :                                     struct TALER_Amount *authorized_amount)
    1517             : {
    1518           8 :   struct PostgresClosure *pg = cls;
    1519           8 :   struct GNUNET_PQ_QueryParam params[] = {
    1520             :     GNUNET_PQ_query_param_auto_from_type (reserve_priv),
    1521             :     GNUNET_PQ_query_param_end
    1522             :   };
    1523             :   enum GNUNET_DB_QueryStatus qs;
    1524           8 :   struct GetAuthorizedTipAmountContext ctx = { 0 };
    1525             : 
    1526           8 :   check_connection (pg);
    1527           8 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    1528             :                                              "find_tip_authorizations",
    1529             :                                              params,
    1530             :                                              &find_tip_authorizations_cb,
    1531             :                                              &ctx);
    1532           8 :   if (0 >= qs)
    1533           0 :     return qs;
    1534           8 :   memcpy (authorized_amount, &ctx.authorized_amount, sizeof (struct TALER_Amount));
    1535           8 :   return ctx.qs;
    1536             : }
    1537             : 
    1538             : 
    1539             : /**
    1540             :  * Return proposals whose timestamp are older than `date`.
    1541             :  * The rows are sorted having the youngest first.
    1542             :  *
    1543             :  * @param cls our plugin handle.
    1544             :  * @param date only results older than this date are returned.
    1545             :  * @param merchant_pub instance's public key; only rows related to this
    1546             :  * instance are returned.
    1547             :  * @param nrows at most nrows rows are returned.
    1548             :  * @param cb function to call with transaction data, can be NULL.
    1549             :  * @param cb_cls closure for @a cb
    1550             :  * @return transaction status
    1551             :  */
    1552             : static enum GNUNET_DB_QueryStatus
    1553           1 : postgres_find_contract_terms_by_date (void *cls,
    1554             :                                       struct GNUNET_TIME_Absolute date,
    1555             :                                       const struct TALER_MerchantPublicKeyP *merchant_pub,
    1556             :                                       uint64_t nrows,
    1557             :                                       TALER_MERCHANTDB_ProposalDataCallback cb,
    1558             :                                       void *cb_cls)
    1559             : {
    1560           1 :   struct PostgresClosure *pg = cls;
    1561           1 :   struct GNUNET_PQ_QueryParam params[] = {
    1562             :     GNUNET_PQ_query_param_absolute_time (&date),
    1563             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    1564             :     GNUNET_PQ_query_param_uint64 (&nrows),
    1565             :     GNUNET_PQ_query_param_end
    1566             :   };
    1567             :   enum GNUNET_DB_QueryStatus qs;
    1568           1 :   struct FindContractsContext fcctx = {
    1569             :     .cb = cb,
    1570             :     .cb_cls = cb_cls
    1571             :   };
    1572             : 
    1573           1 :   check_connection (pg);
    1574           1 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    1575             :                                              "find_contract_terms_by_date",
    1576             :                                              params,
    1577             :                                              &find_contracts_cb,
    1578             :                                              &fcctx);
    1579           1 :   if (0 >= qs)
    1580           1 :     return qs;
    1581           0 :   return fcctx.qs;
    1582             : }
    1583             : 
    1584             : 
    1585             : /**
    1586             :  * Closure for #find_payments_cb().
    1587             :  */
    1588             : struct FindPaymentsContext
    1589             : {
    1590             :   /**
    1591             :    * Function to call with results.
    1592             :    */
    1593             :   TALER_MERCHANTDB_CoinDepositCallback cb;
    1594             : 
    1595             :   /**
    1596             :    * Closure for @e cls.
    1597             :    */
    1598             :   void *cb_cls;
    1599             : 
    1600             :   /**
    1601             :    * Contract term hash used for the search.
    1602             :    */
    1603             :   const struct GNUNET_HashCode *h_contract_terms;
    1604             : 
    1605             :   /**
    1606             :    * Transaction status (set).
    1607             :    */
    1608             :   enum GNUNET_DB_QueryStatus qs;
    1609             : };
    1610             : 
    1611             : 
    1612             : /**
    1613             :  * Function to be called with the results of a SELECT statement
    1614             :  * that has returned @a num_results results.
    1615             :  *
    1616             :  * @param cls of type `struct FindPaymentsContext *`
    1617             :  * @param result the postgres result
    1618             :  * @param num_result the number of results in @a result
    1619             :  */
    1620             : static void
    1621          57 : find_payments_cb (void *cls,
    1622             :                   PGresult *result,
    1623             :                   unsigned int num_results)
    1624             : {
    1625          57 :   struct FindPaymentsContext *fpc = cls;
    1626             : 
    1627          98 :   for (unsigned int i=0;i<num_results;i++)
    1628             :   {
    1629             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    1630             :     struct TALER_Amount amount_with_fee;
    1631             :     struct TALER_Amount deposit_fee;
    1632             :     struct TALER_Amount refund_fee;
    1633             :     struct TALER_Amount wire_fee;
    1634             :     json_t *exchange_proof;
    1635             :     char *exchange_url;
    1636          41 :     struct GNUNET_PQ_ResultSpec rs[] = {
    1637             :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    1638             :                                             &coin_pub),
    1639             :       GNUNET_PQ_result_spec_string ("exchange_url",
    1640             :                                     &exchange_url),
    1641             :       TALER_PQ_result_spec_amount ("amount_with_fee",
    1642             :                                    &amount_with_fee),
    1643             :       TALER_PQ_result_spec_amount ("deposit_fee",
    1644             :                                    &deposit_fee),
    1645             :       TALER_PQ_result_spec_amount ("refund_fee",
    1646             :                                    &refund_fee),
    1647             :       TALER_PQ_result_spec_amount ("wire_fee",
    1648             :                                    &wire_fee),
    1649             :       TALER_PQ_result_spec_json ("exchange_proof",
    1650             :                                  &exchange_proof),
    1651             :       GNUNET_PQ_result_spec_end
    1652             :     };
    1653             : 
    1654          41 :     if (GNUNET_OK !=
    1655          41 :         GNUNET_PQ_extract_result (result,
    1656             :                                   rs,
    1657             :                                   i))
    1658             :     {
    1659           0 :       GNUNET_break (0);
    1660           0 :       fpc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1661           0 :       return;
    1662             :     }
    1663          41 :     fpc->qs = i + 1;
    1664          41 :     fpc->cb (fpc->cb_cls,
    1665             :              fpc->h_contract_terms,
    1666             :              &coin_pub,
    1667             :              exchange_url,
    1668             :              &amount_with_fee,
    1669             :              &deposit_fee,
    1670             :              &refund_fee,
    1671             :              &wire_fee,
    1672             :              exchange_proof);
    1673          41 :     GNUNET_PQ_cleanup_result (rs);
    1674             :   }
    1675             : }
    1676             : 
    1677             : 
    1678             : /**
    1679             :  * Lookup information about coin payments by proposal data hash
    1680             :  * (and @a merchant_pub)
    1681             :  *
    1682             :  * @param cls closure
    1683             :  * @param h_contract_terms key for the search
    1684             :  * @param merchant_pub merchant's public key
    1685             :  * @param cb function to call with payment data
    1686             :  * @param cb_cls closure for @a cb
    1687             :  * @return transaction status
    1688             :  */
    1689             : static enum GNUNET_DB_QueryStatus
    1690          57 : postgres_find_payments (void *cls,
    1691             :                         const struct GNUNET_HashCode *h_contract_terms,
    1692             :                         const struct TALER_MerchantPublicKeyP *merchant_pub,
    1693             :                         TALER_MERCHANTDB_CoinDepositCallback cb,
    1694             :                         void *cb_cls)
    1695             : {
    1696          57 :   struct PostgresClosure *pg = cls;
    1697          57 :   struct GNUNET_PQ_QueryParam params[] = {
    1698             :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    1699             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    1700             :     GNUNET_PQ_query_param_end
    1701             :   };
    1702          57 :   struct FindPaymentsContext fpc = {
    1703             :     .h_contract_terms = h_contract_terms,
    1704             :     .cb = cb,
    1705             :     .cb_cls = cb_cls
    1706             :   };
    1707             :   enum GNUNET_DB_QueryStatus qs;
    1708             : 
    1709          57 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1710             :               "Finding payment for h_contract_terms '%s'\n",
    1711             :               GNUNET_h2s (h_contract_terms));
    1712          57 :   check_connection (pg);
    1713          57 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    1714             :                                              "find_deposits",
    1715             :                                              params,
    1716             :                                              &find_payments_cb,
    1717             :                                              &fpc);
    1718          57 :   if (qs <= 0)
    1719          19 :     return qs;
    1720          38 :   return fpc.qs;
    1721             : }
    1722             : 
    1723             : 
    1724             : /**
    1725             :  * Closure for #find_payments_by_coin_cb().
    1726             :  */
    1727             : struct FindPaymentsByCoinContext
    1728             : {
    1729             :   /**
    1730             :    * Function to call with results.
    1731             :    */
    1732             :   TALER_MERCHANTDB_CoinDepositCallback cb;
    1733             : 
    1734             :   /**
    1735             :    * Closure for @e cls.
    1736             :    */
    1737             :   void *cb_cls;
    1738             : 
    1739             :   /**
    1740             :    * Coin we are looking for.
    1741             :    */
    1742             :   const struct TALER_CoinSpendPublicKeyP *coin_pub;
    1743             : 
    1744             :   /**
    1745             :    * Hash of the contract we are looking for.
    1746             :    */
    1747             :   const struct GNUNET_HashCode *h_contract_terms;
    1748             : 
    1749             :   /**
    1750             :    * Transaction status (set).
    1751             :    */
    1752             :   enum GNUNET_DB_QueryStatus qs;
    1753             : };
    1754             : 
    1755             : 
    1756             : /**
    1757             :  * Function to be called with the results of a SELECT statement
    1758             :  * that has returned @a num_results results.
    1759             :  *
    1760             :  * @param cls of type `struct FindPaymentsByCoinContext *`
    1761             :  * @param result the postgres result
    1762             :  * @param num_result the number of results in @a result
    1763             :  */
    1764             : static void
    1765           3 : find_payments_by_coin_cb (void *cls,
    1766             :                           PGresult *result,
    1767             :                           unsigned int num_results)
    1768             : {
    1769           3 :   struct FindPaymentsByCoinContext *fpc = cls;
    1770             : 
    1771           6 :   for (unsigned int i=0;i<num_results;i++)
    1772             :   {
    1773             :     struct TALER_Amount amount_with_fee;
    1774             :     struct TALER_Amount deposit_fee;
    1775             :     struct TALER_Amount refund_fee;
    1776             :     struct TALER_Amount wire_fee;
    1777             :     char *exchange_url;
    1778             :     json_t *exchange_proof;
    1779           3 :     struct GNUNET_PQ_ResultSpec rs[] = {
    1780             :       TALER_PQ_result_spec_amount ("amount_with_fee",
    1781             :                                    &amount_with_fee),
    1782             :       TALER_PQ_result_spec_amount ("deposit_fee",
    1783             :                                    &deposit_fee),
    1784             :       TALER_PQ_result_spec_amount ("refund_fee",
    1785             :                                    &refund_fee),
    1786             :       TALER_PQ_result_spec_amount ("wire_fee",
    1787             :                                    &wire_fee),
    1788             :       GNUNET_PQ_result_spec_string ("exchange_url",
    1789             :                                     &exchange_url),
    1790             :       TALER_PQ_result_spec_json ("exchange_proof",
    1791             :                                  &exchange_proof),
    1792             :       GNUNET_PQ_result_spec_end
    1793             :     };
    1794             : 
    1795           3 :     if (GNUNET_OK !=
    1796           3 :         GNUNET_PQ_extract_result (result,
    1797             :                                   rs,
    1798             :                                   i))
    1799             :     {
    1800           0 :       GNUNET_break (0);
    1801           0 :       fpc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1802           0 :       return;
    1803             :     }
    1804           3 :     fpc->qs = i + 1;
    1805           3 :     fpc->cb (fpc->cb_cls,
    1806             :              fpc->h_contract_terms,
    1807             :              fpc->coin_pub,
    1808             :              exchange_url,
    1809             :              &amount_with_fee,
    1810             :              &deposit_fee,
    1811             :              &refund_fee,
    1812             :              &wire_fee,
    1813             :              exchange_proof);
    1814           3 :     GNUNET_PQ_cleanup_result (rs);
    1815             :   }
    1816             : }
    1817             : 
    1818             : 
    1819             : /**
    1820             :  * Retrieve information about a deposited coin.
    1821             :  *
    1822             :  * @param cls closure
    1823             :  * @param h_contract_terms hashcode of the proposal data paid by @a coin_pub
    1824             :  * @param merchant_pub merchant's public key.
    1825             :  * @param coin_pub coin's public key used for the search
    1826             :  * @param cb function to call with payment data
    1827             :  * @param cb_cls closure for @a cb
    1828             :  * @return transaction status
    1829             :  */
    1830             : static enum GNUNET_DB_QueryStatus
    1831           3 : postgres_find_payments_by_hash_and_coin (void *cls,
    1832             :                                          const struct GNUNET_HashCode *h_contract_terms,
    1833             :                                          const struct TALER_MerchantPublicKeyP *merchant_pub,
    1834             :                                          const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1835             :                                          TALER_MERCHANTDB_CoinDepositCallback cb,
    1836             :                                          void *cb_cls)
    1837             : {
    1838           3 :   struct PostgresClosure *pg = cls;
    1839           3 :   struct GNUNET_PQ_QueryParam params[] = {
    1840             :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    1841             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    1842             :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    1843             :     GNUNET_PQ_query_param_end
    1844             :   };
    1845           3 :   struct FindPaymentsByCoinContext fpc = {
    1846             :     .cb = cb,
    1847             :     .cb_cls = cb_cls,
    1848             :     .h_contract_terms = h_contract_terms,
    1849             :     .coin_pub = coin_pub
    1850             :   };
    1851             :   enum GNUNET_DB_QueryStatus qs;
    1852             : 
    1853           3 :   check_connection (pg);
    1854           3 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    1855             :                                              "find_deposits_by_hash_and_coin",
    1856             :                                              params,
    1857             :                                              &find_payments_by_coin_cb,
    1858             :                                              &fpc);
    1859           3 :   if (0 >= qs)
    1860           0 :     return qs;
    1861           3 :   return fpc.qs;
    1862             : }
    1863             : 
    1864             : 
    1865             : /**
    1866             :  * Closure for #find_transfers_cb().
    1867             :  */
    1868             : struct FindTransfersContext
    1869             : {
    1870             :   /**
    1871             :    * Function to call on results.
    1872             :    */
    1873             :   TALER_MERCHANTDB_TransferCallback cb;
    1874             : 
    1875             :   /**
    1876             :    * Closure for @e cb.
    1877             :    */
    1878             :   void *cb_cls;
    1879             : 
    1880             :   /**
    1881             :    * Hash of the contract we are looking under.
    1882             :    */
    1883             :   const struct GNUNET_HashCode *h_contract_terms;
    1884             : 
    1885             :   /**
    1886             :    * Transaction status (set).
    1887             :    */
    1888             :   enum GNUNET_DB_QueryStatus qs;
    1889             : };
    1890             : 
    1891             : 
    1892             : /**
    1893             :  * Function to be called with the results of a SELECT statement
    1894             :  * that has returned @a num_results results.
    1895             :  *
    1896             :  * @param cls of type `struct FindTransfersContext *`
    1897             :  * @param result the postgres result
    1898             :  * @param num_result the number of results in @a result
    1899             :  */
    1900             : static void
    1901           7 : find_transfers_cb (void *cls,
    1902             :                    PGresult *result,
    1903             :                    unsigned int num_results)
    1904             : {
    1905           7 :   struct FindTransfersContext *ftc = cls;
    1906             : 
    1907          11 :   for (unsigned int i=0;i<PQntuples (result);i++)
    1908             :   {
    1909             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    1910             :     struct TALER_WireTransferIdentifierRawP wtid;
    1911             :     struct GNUNET_TIME_Absolute execution_time;
    1912             :     json_t *proof;
    1913             : 
    1914           4 :     struct GNUNET_PQ_ResultSpec rs[] = {
    1915             :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    1916             :                                             &coin_pub),
    1917             :       GNUNET_PQ_result_spec_auto_from_type ("wtid",
    1918             :                                             &wtid),
    1919             :       GNUNET_PQ_result_spec_absolute_time ("execution_time",
    1920             :                                            &execution_time),
    1921             :       TALER_PQ_result_spec_json ("proof",
    1922             :                                  &proof),
    1923             :       GNUNET_PQ_result_spec_end
    1924             :     };
    1925             : 
    1926           4 :     if (GNUNET_OK !=
    1927           4 :         GNUNET_PQ_extract_result (result,
    1928             :                                   rs,
    1929             :                                   i))
    1930             :     {
    1931           0 :       GNUNET_break (0);
    1932           0 :       ftc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1933           0 :       return;
    1934             :     }
    1935           4 :     ftc->qs = i + 1;
    1936           4 :     ftc->cb (ftc->cb_cls,
    1937             :              ftc->h_contract_terms,
    1938             :              &coin_pub,
    1939             :              &wtid,
    1940             :              execution_time,
    1941             :              proof);
    1942           4 :     GNUNET_PQ_cleanup_result (rs);
    1943             :   }
    1944             : }
    1945             : 
    1946             : 
    1947             : /**
    1948             :  * Lookup information about a transfer by @a h_contract_terms.  Note
    1949             :  * that in theory there could be multiple wire transfers for a
    1950             :  * single @a h_contract_terms, as the transaction may have involved
    1951             :  * multiple coins and the coins may be spread over different wire
    1952             :  * transfers.
    1953             :  *
    1954             :  * @param cls closure
    1955             :  * @param h_contract_terms key for the search
    1956             :  * @param cb function to call with transfer data
    1957             :  * @param cb_cls closure for @a cb
    1958             :  * @return transaction status
    1959             :  */
    1960             : static enum GNUNET_DB_QueryStatus
    1961           7 : postgres_find_transfers_by_hash (void *cls,
    1962             :                                  const struct GNUNET_HashCode *h_contract_terms,
    1963             :                                  TALER_MERCHANTDB_TransferCallback cb,
    1964             :                                  void *cb_cls)
    1965             : {
    1966           7 :   struct PostgresClosure *pg = cls;
    1967           7 :   struct GNUNET_PQ_QueryParam params[] = {
    1968             :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    1969             :     GNUNET_PQ_query_param_end
    1970             :   };
    1971           7 :   struct FindTransfersContext ftc = {
    1972             :     .h_contract_terms = h_contract_terms,
    1973             :     .cb = cb,
    1974             :     .cb_cls = cb_cls
    1975             :   };
    1976             :   enum GNUNET_DB_QueryStatus qs;
    1977             : 
    1978           7 :   check_connection (pg);
    1979           7 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    1980             :                                              "find_transfers_by_hash",
    1981             :                                              params,
    1982             :                                              &find_transfers_cb,
    1983             :                                              &ftc);
    1984           7 :   if (0 >= qs)
    1985           3 :     return qs;
    1986           4 :   return ftc.qs;
    1987             : }
    1988             : 
    1989             : 
    1990             : /**
    1991             :  * Closure for #find_deposits_cb().
    1992             :  */
    1993             : struct FindDepositsContext
    1994             : {
    1995             : 
    1996             :   /**
    1997             :    * Function to call for each result.
    1998             :    */
    1999             :   TALER_MERCHANTDB_CoinDepositCallback cb;
    2000             : 
    2001             :   /**
    2002             :    * Closure for @e cb.
    2003             :    */
    2004             :   void *cb_cls;
    2005             : 
    2006             :   /**
    2007             :    * Transaction status (set).
    2008             :    */
    2009             :   enum GNUNET_DB_QueryStatus qs;
    2010             : };
    2011             : 
    2012             : 
    2013             : /**
    2014             :  * Function to be called with the results of a SELECT statement
    2015             :  * that has returned @a num_results results.
    2016             :  *
    2017             :  * @param cls of type `struct FindDepositsContext *`
    2018             :  * @param result the postgres result
    2019             :  * @param num_result the number of results in @a result
    2020             :  */
    2021             : static void
    2022           1 : find_deposits_cb (void *cls,
    2023             :                   PGresult *result,
    2024             :                   unsigned int num_results)
    2025             : {
    2026           1 :   struct FindDepositsContext *fdc = cls;
    2027             : 
    2028           2 :   for (unsigned int i=0;i<PQntuples (result);i++)
    2029             :   {
    2030             :     struct GNUNET_HashCode h_contract_terms;
    2031             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    2032             :     struct TALER_Amount amount_with_fee;
    2033             :     struct TALER_Amount deposit_fee;
    2034             :     struct TALER_Amount refund_fee;
    2035             :     struct TALER_Amount wire_fee;
    2036             :     char *exchange_url;
    2037             :     json_t *exchange_proof;
    2038           1 :     struct GNUNET_PQ_ResultSpec rs[] = {
    2039             :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    2040             :                                             &h_contract_terms),
    2041             :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    2042             :                                             &coin_pub),
    2043             :       TALER_PQ_result_spec_amount ("amount_with_fee",
    2044             :                                    &amount_with_fee),
    2045             :       TALER_PQ_result_spec_amount ("deposit_fee",
    2046             :                                    &deposit_fee),
    2047             :       TALER_PQ_result_spec_amount ("refund_fee",
    2048             :                                    &refund_fee),
    2049             :       TALER_PQ_result_spec_amount ("wire_fee",
    2050             :                                    &wire_fee),
    2051             :       GNUNET_PQ_result_spec_string ("exchange_url",
    2052             :                                     &exchange_url),
    2053             :       TALER_PQ_result_spec_json ("exchange_proof",
    2054             :                                  &exchange_proof),
    2055             :       GNUNET_PQ_result_spec_end
    2056             :     };
    2057             : 
    2058           1 :     if (GNUNET_OK !=
    2059           1 :         GNUNET_PQ_extract_result (result,
    2060             :                                   rs,
    2061             :                                   i))
    2062             :     {
    2063           0 :       GNUNET_break (0);
    2064           0 :       fdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2065           0 :       return;
    2066             :     }
    2067           1 :     fdc->qs = i + 1;
    2068           1 :     fdc->cb (fdc->cb_cls,
    2069             :              &h_contract_terms,
    2070             :              &coin_pub,
    2071             :              exchange_url,
    2072             :              &amount_with_fee,
    2073             :              &deposit_fee,
    2074             :              &refund_fee,
    2075             :              &wire_fee,
    2076             :              exchange_proof);
    2077           1 :     GNUNET_PQ_cleanup_result (rs);
    2078             :   }
    2079             : }
    2080             : 
    2081             : 
    2082             : /**
    2083             :  * Lookup information about a coin deposits by @a wtid.
    2084             :  *
    2085             :  * @param cls closure
    2086             :  * @param wtid wire transfer identifier to find matching transactions for
    2087             :  * @param cb function to call with payment data
    2088             :  * @param cb_cls closure for @a cb
    2089             :  * @return transaction status
    2090             :  */
    2091             : static enum GNUNET_DB_QueryStatus
    2092           1 : postgres_find_deposits_by_wtid (void *cls,
    2093             :                                 const struct TALER_WireTransferIdentifierRawP *wtid,
    2094             :                                 TALER_MERCHANTDB_CoinDepositCallback cb,
    2095             :                                 void *cb_cls)
    2096             : {
    2097           1 :   struct PostgresClosure *pg = cls;
    2098           1 :   struct GNUNET_PQ_QueryParam params[] = {
    2099             :     GNUNET_PQ_query_param_auto_from_type (wtid),
    2100             :     GNUNET_PQ_query_param_end
    2101             :   };
    2102           1 :   struct FindDepositsContext fdc = {
    2103             :     .cb = cb,
    2104             :     .cb_cls = cb_cls
    2105             :   };
    2106             :   enum GNUNET_DB_QueryStatus qs;
    2107             : 
    2108           1 :   check_connection (pg);
    2109           1 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    2110             :                                              "find_deposits_by_wtid",
    2111             :                                              params,
    2112             :                                              &find_deposits_cb,
    2113             :                                              &fdc);
    2114           1 :   if (0 >= qs)
    2115           0 :     return qs;
    2116           1 :   return fdc.qs;
    2117             : }
    2118             : 
    2119             : 
    2120             : /**
    2121             :  * Closure for #get_refunds_cb().
    2122             :  */
    2123             : struct GetRefundsContext
    2124             : {
    2125             :   /**
    2126             :    * Function to call for each refund.
    2127             :    */
    2128             :   TALER_MERCHANTDB_RefundCallback rc;
    2129             : 
    2130             :   /**
    2131             :    * Closure for @e rc.
    2132             :    */
    2133             :   void *rc_cls;
    2134             : 
    2135             :   /**
    2136             :    * Transaction result.
    2137             :    */
    2138             :   enum GNUNET_DB_QueryStatus qs;
    2139             : };
    2140             : 
    2141             : 
    2142             : /**
    2143             :  * Function to be called with the results of a SELECT statement
    2144             :  * that has returned @a num_results results.
    2145             :  *
    2146             :  * @param cls of type `struct GetRefundsContext *`
    2147             :  * @param result the postgres result
    2148             :  * @param num_result the number of results in @a result
    2149             :  */
    2150             : static void
    2151          74 : get_refunds_cb (void *cls,
    2152             :                 PGresult *result,
    2153             :                 unsigned int num_results)
    2154             : {
    2155          74 :   struct GetRefundsContext *grc = cls;
    2156             : 
    2157          77 :   for (unsigned int i=0;i<num_results;i++)
    2158             :   {
    2159             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    2160             :     uint64_t rtransaction_id;
    2161             :     struct TALER_Amount refund_amount;
    2162             :     struct TALER_Amount refund_fee;
    2163             :     char *reason;
    2164           3 :     struct GNUNET_PQ_ResultSpec rs[] = {
    2165             :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    2166             :                                             &coin_pub),
    2167             :       GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
    2168             :                                     &rtransaction_id),
    2169             :       TALER_PQ_result_spec_amount ("refund_amount",
    2170             :                                    &refund_amount),
    2171             :       TALER_PQ_result_spec_amount ("refund_fee",
    2172             :                                    &refund_fee),
    2173             :       GNUNET_PQ_result_spec_string ("reason",
    2174             :                                     &reason),
    2175             :       GNUNET_PQ_result_spec_end
    2176             :     };
    2177             : 
    2178           3 :     if (GNUNET_OK !=
    2179           3 :         GNUNET_PQ_extract_result (result,
    2180             :                                   rs,
    2181             :                                   i))
    2182             :     {
    2183           0 :       GNUNET_break (0);
    2184           0 :       grc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2185           0 :       return;
    2186             :     }
    2187           3 :     grc->qs = i + 1;
    2188           3 :     grc->rc (grc->rc_cls,
    2189             :              &coin_pub,
    2190             :              rtransaction_id,
    2191             :              reason,
    2192             :              &refund_amount,
    2193             :              &refund_fee);
    2194           3 :     GNUNET_PQ_cleanup_result (rs);
    2195             :   }
    2196             : }
    2197             : 
    2198             : 
    2199             : /**
    2200             :  * Obtain refunds associated with a contract.
    2201             :  *
    2202             :  * @param cls closure, typically a connection to the db
    2203             :  * @param merchant_pub public key of the merchant instance
    2204             :  * @param h_contract_terms hash code of the contract
    2205             :  * @param rc function to call for each coin on which there is a refund
    2206             :  * @param rc_cls closure for @a rc
    2207             :  * @return transaction status
    2208             :  */
    2209             : static enum GNUNET_DB_QueryStatus
    2210          74 : postgres_get_refunds_from_contract_terms_hash (void *cls,
    2211             :                                                const struct TALER_MerchantPublicKeyP *merchant_pub,
    2212             :                                                const struct GNUNET_HashCode *h_contract_terms,
    2213             :                                                TALER_MERCHANTDB_RefundCallback rc,
    2214             :                                                void *rc_cls)
    2215             : {
    2216          74 :   struct PostgresClosure *pg = cls;
    2217          74 :   struct GNUNET_PQ_QueryParam params[] = {
    2218             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    2219             :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    2220             :     GNUNET_PQ_query_param_end
    2221             :   };
    2222          74 :   struct GetRefundsContext grc = {
    2223             :     .rc = rc,
    2224             :     .rc_cls = rc_cls
    2225             :   };
    2226             :   enum GNUNET_DB_QueryStatus qs;
    2227             : 
    2228          74 :   TALER_LOG_DEBUG ("Looking for refund %s + %s\n",
    2229             :                    GNUNET_h2s (h_contract_terms),
    2230             :                    TALER_B2S (merchant_pub));
    2231          74 :   check_connection (pg);
    2232          74 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    2233             :                                              "find_refunds_from_contract_terms_hash",
    2234             :                                              params,
    2235             :                                              &get_refunds_cb,
    2236             :                                              &grc);
    2237          74 :   if (0 >= qs)
    2238          71 :     return qs;
    2239           3 :   return grc.qs;
    2240             : }
    2241             : 
    2242             : 
    2243             : /**
    2244             :  * Insert a refund row into merchant_refunds.  Not meant to be exported
    2245             :  * in the db API.
    2246             :  *
    2247             :  * @param cls closure, tipically a connection to the db
    2248             :  * @param merchant_pub merchant instance public key
    2249             :  * @param h_contract_terms hashcode of the contract related to this refund
    2250             :  * @param coin_pub public key of the coin giving the (part of) refund
    2251             :  * @param reason human readable explaination behind the refund
    2252             :  * @param refund how much this coin is refunding
    2253             :  * @param refund_fee refund fee for this coin
    2254             :  */
    2255             : static enum GNUNET_DB_QueryStatus
    2256           9 : insert_refund (void *cls,
    2257             :                const struct TALER_MerchantPublicKeyP *merchant_pub,
    2258             :                const struct GNUNET_HashCode *h_contract_terms,
    2259             :                const struct TALER_CoinSpendPublicKeyP *coin_pub,
    2260             :                const char *reason,
    2261             :                const struct TALER_Amount *refund,
    2262             :                const struct TALER_Amount *refund_fee)
    2263             : {
    2264           9 :   struct PostgresClosure *pg = cls;
    2265           9 :   struct GNUNET_PQ_QueryParam params[] = {
    2266             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    2267             :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    2268             :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    2269             :     GNUNET_PQ_query_param_string (reason),
    2270             :     TALER_PQ_query_param_amount (refund),
    2271             :     TALER_PQ_query_param_amount (refund_fee),
    2272             :     GNUNET_PQ_query_param_end
    2273             :   };
    2274             : 
    2275           9 :   TALER_LOG_DEBUG ("Inserting refund %s + %s\n",
    2276             :                    GNUNET_h2s (h_contract_terms),
    2277             :                    TALER_B2S (merchant_pub));
    2278             : 
    2279           9 :   check_connection (pg);
    2280           9 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    2281             :                                              "insert_refund",
    2282             :                                              params);
    2283             : }
    2284             : 
    2285             : 
    2286             : /**
    2287             :  * Store information about wire fees charged by an exchange,
    2288             :  * including signature (so we have proof).
    2289             :  *
    2290             :  * @param cls closure
    2291             :  * @paramm exchange_pub public key of the exchange
    2292             :  * @param h_wire_method hash of wire method
    2293             :  * @param wire_fee wire fee charged
    2294             :  * @param closing_fee closing fee charged (irrelevant for us,
    2295             :  *              but needed to check signature)
    2296             :  * @param start_date start of fee being used
    2297             :  * @param end_date end of fee being used
    2298             :  * @param exchange_sig signature of exchange over fee structure
    2299             :  * @return transaction status code
    2300             :  */
    2301             : static enum GNUNET_DB_QueryStatus
    2302          10 : postgres_store_wire_fee_by_exchange (void *cls,
    2303             :                                      const struct TALER_MasterPublicKeyP *exchange_pub,
    2304             :                                      const struct GNUNET_HashCode *h_wire_method,
    2305             :                                      const struct TALER_Amount *wire_fee,
    2306             :                                      const struct TALER_Amount *closing_fee,
    2307             :                                      struct GNUNET_TIME_Absolute start_date,
    2308             :                                      struct GNUNET_TIME_Absolute end_date,
    2309             :                                      const struct TALER_MasterSignatureP *exchange_sig)
    2310             : {
    2311          10 :   struct PostgresClosure *pg = cls;
    2312          10 :   struct GNUNET_PQ_QueryParam params[] = {
    2313             :     GNUNET_PQ_query_param_auto_from_type (exchange_pub),
    2314             :     GNUNET_PQ_query_param_auto_from_type (h_wire_method),
    2315             :     TALER_PQ_query_param_amount (wire_fee),
    2316             :     TALER_PQ_query_param_amount (closing_fee),
    2317             :     GNUNET_PQ_query_param_absolute_time (&start_date),
    2318             :     GNUNET_PQ_query_param_absolute_time (&end_date),
    2319             :     GNUNET_PQ_query_param_auto_from_type (exchange_sig),
    2320             :     GNUNET_PQ_query_param_end
    2321             :   };
    2322             : 
    2323          10 :   check_connection (pg);
    2324          10 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2325             :               "Storing wire fee for %s starting at %s of %s\n",
    2326             :               TALER_B2S (exchange_pub),
    2327             :               GNUNET_STRINGS_absolute_time_to_string (start_date),
    2328             :               TALER_amount2s (wire_fee));
    2329          10 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    2330             :                                              "insert_wire_fee",
    2331             :                                              params);
    2332             : }
    2333             : 
    2334             : 
    2335             : /**
    2336             :  * Obtain information about wire fees charged by an exchange,
    2337             :  * including signature (so we have proof).
    2338             :  *
    2339             :  * @param cls closure
    2340             :  * @param exchange_pub public key of the exchange
    2341             :  * @param h_wire_method hash of wire method
    2342             :  * @param contract_date date of the contract to use for the lookup
    2343             :  * @param[out] wire_fee wire fee charged
    2344             :  * @param[out] closing_fee closing fee charged (irrelevant for us,
    2345             :  *              but needed to check signature)
    2346             :  * @param[out] start_date start of fee being used
    2347             :  * @param[out] end_date end of fee being used
    2348             :  * @param[out] exchange_sig signature of exchange over fee structure
    2349             :  * @return transaction status code
    2350             :  */
    2351             : static enum GNUNET_DB_QueryStatus
    2352           6 : postgres_lookup_wire_fee (void *cls,
    2353             :                           const struct TALER_MasterPublicKeyP *exchange_pub,
    2354             :                           const struct GNUNET_HashCode *h_wire_method,
    2355             :                           struct GNUNET_TIME_Absolute contract_date,
    2356             :                           struct TALER_Amount *wire_fee,
    2357             :                           struct TALER_Amount *closing_fee,
    2358             :                           struct GNUNET_TIME_Absolute *start_date,
    2359             :                           struct GNUNET_TIME_Absolute *end_date,
    2360             :                           struct TALER_MasterSignatureP *exchange_sig)
    2361             : {
    2362           6 :   struct PostgresClosure *pg = cls;
    2363           6 :   struct GNUNET_PQ_QueryParam params[] = {
    2364             :     GNUNET_PQ_query_param_auto_from_type (exchange_pub),
    2365             :     GNUNET_PQ_query_param_auto_from_type (h_wire_method),
    2366             :     GNUNET_PQ_query_param_absolute_time (&contract_date),
    2367             :     GNUNET_PQ_query_param_end
    2368             :   };
    2369           6 :   struct GNUNET_PQ_ResultSpec rs[] = {
    2370             :     TALER_PQ_result_spec_amount ("wire_fee",
    2371             :                                  wire_fee),
    2372             :     TALER_PQ_result_spec_amount ("closing_fee",
    2373             :                                  closing_fee),
    2374             :     GNUNET_PQ_result_spec_absolute_time ("start_date",
    2375             :                                          start_date),
    2376             :     GNUNET_PQ_result_spec_absolute_time ("end_date",
    2377             :                                          end_date),
    2378             :     GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
    2379             :                                           exchange_sig),
    2380             :     GNUNET_PQ_result_spec_end
    2381             :   };
    2382             : 
    2383           6 :   check_connection (pg);
    2384           6 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    2385             :                                                    "lookup_wire_fee",
    2386             :                                                    params,
    2387             :                                                    rs);
    2388             : }
    2389             : 
    2390             : 
    2391             : /**
    2392             :  * Closure for #process_refund_cb.
    2393             :  */
    2394             : struct FindRefundContext
    2395             : {
    2396             :   /**
    2397             :    * Updated to reflect total amount refunded so far.
    2398             :    */
    2399             :   struct TALER_Amount refunded_amount;
    2400             : 
    2401             :   /**
    2402             :    * Set to #GNUNET_SYSERR on hard errors.
    2403             :    */
    2404             :   int err;
    2405             : };
    2406             : 
    2407             : 
    2408             : /**
    2409             :  * Function to be called with the results of a SELECT statement
    2410             :  * that has returned @a num_results results.
    2411             :  *
    2412             :  * @param cls closure, our `struct FindRefundContext`
    2413             :  * @param result the postgres result
    2414             :  * @param num_result the number of results in @a result
    2415             :  */
    2416             : static void
    2417          11 : process_refund_cb (void *cls,
    2418             :                    PGresult *result,
    2419             :                    unsigned int num_results)
    2420             : {
    2421          11 :   struct FindRefundContext *ictx = cls;
    2422             : 
    2423          32 :   for (unsigned int i=0; i<num_results; i++)
    2424             :   {
    2425             :     /* Sum up existing refunds */
    2426             :     struct TALER_Amount acc;
    2427           5 :     struct GNUNET_PQ_ResultSpec rs[] = {
    2428             :       TALER_PQ_result_spec_amount ("refund_amount",
    2429             :                                    &acc),
    2430             :       GNUNET_PQ_result_spec_end
    2431             :     };
    2432             : 
    2433           5 :     if (GNUNET_OK !=
    2434           5 :         GNUNET_PQ_extract_result (result,
    2435             :                                   rs,
    2436             :                                   i))
    2437             :     {
    2438           0 :       GNUNET_break (0);
    2439           0 :       ictx->err = GNUNET_SYSERR;
    2440           0 :       return;
    2441             :     }
    2442           5 :     if (GNUNET_SYSERR ==
    2443           5 :         TALER_amount_add (&ictx->refunded_amount,
    2444           5 :                           &ictx->refunded_amount,
    2445             :                           &acc))
    2446             :     {
    2447           0 :       GNUNET_break (0);
    2448           0 :       ictx->err = GNUNET_SYSERR;
    2449           0 :       return;
    2450             :     }
    2451           5 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2452             :                 "Found refund of %s\n",
    2453             :                 TALER_amount2s (&acc));
    2454             :   }
    2455             : }
    2456             : 
    2457             : 
    2458             : /**
    2459             :  * Closure for #process_deposits_cb.
    2460             :  */
    2461             : struct InsertRefundContext
    2462             : {
    2463             :   /**
    2464             :    * Used to provide a connection to the db
    2465             :    */
    2466             :   struct PostgresClosure *pg;
    2467             : 
    2468             :   /**
    2469             :    * Amount to which increase the refund for this contract
    2470             :    */
    2471             :   const struct TALER_Amount *refund;
    2472             : 
    2473             :   /**
    2474             :    * Merchant instance public key
    2475             :    */
    2476             :   const struct TALER_MerchantPublicKeyP *merchant_pub;
    2477             : 
    2478             :   /**
    2479             :    * Hash code representing the contract
    2480             :    */
    2481             :   const struct GNUNET_HashCode *h_contract_terms;
    2482             : 
    2483             :   /**
    2484             :    * Human-readable reason behind this refund
    2485             :    */
    2486             :   const char *reason;
    2487             : 
    2488             :   /**
    2489             :    * Transaction status code.
    2490             :    */
    2491             :   enum GNUNET_DB_QueryStatus qs;
    2492             : };
    2493             : 
    2494             : 
    2495             : /**
    2496             :  * Function to be called with the results of a SELECT statement
    2497             :  * that has returned @a num_results results.
    2498             :  *
    2499             :  * @param cls closure, our `struct InsertRefundContext`
    2500             :  * @param result the postgres result
    2501             :  * @param num_result the number of results in @a result
    2502             :  */
    2503             : static void
    2504          12 : process_deposits_for_refund_cb (void *cls,
    2505             :                                 PGresult *result,
    2506             :                                 unsigned int num_results)
    2507          12 : {
    2508          12 :   struct InsertRefundContext *ctx = cls;
    2509             :   struct TALER_Amount current_refund;
    2510          12 :   struct TALER_Amount deposit_refund[GNUNET_NZL(num_results)];
    2511          12 :   struct TALER_CoinSpendPublicKeyP deposit_coin_pubs[GNUNET_NZL(num_results)];
    2512          12 :   struct TALER_Amount deposit_amount_with_fee[GNUNET_NZL(num_results)];
    2513          12 :   struct TALER_Amount deposit_refund_fee[GNUNET_NZL(num_results)];
    2514             : 
    2515          12 :   GNUNET_assert (GNUNET_OK ==
    2516             :                  TALER_amount_get_zero (ctx->refund->currency,
    2517             :                                         &current_refund));
    2518             : 
    2519             :   /* Pass 1:  Collect amount of existing refunds into current_refund.
    2520             :    * Also store existing refunded amount for each deposit in deposit_refund. */
    2521             : 
    2522          46 :   for (unsigned int i=0; i<num_results; i++)
    2523             :   {
    2524             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    2525             :     struct TALER_Amount amount_with_fee;
    2526             :     struct TALER_Amount refund_fee;
    2527          11 :     struct GNUNET_PQ_ResultSpec rs[] = {
    2528             :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    2529             :                                             &coin_pub),
    2530             :       TALER_PQ_result_spec_amount ("amount_with_fee",
    2531             :                                    &amount_with_fee),
    2532             :       TALER_PQ_result_spec_amount ("refund_fee",
    2533             :                                    &refund_fee),
    2534             :       GNUNET_PQ_result_spec_end
    2535             :     };
    2536             : 
    2537          11 :     if (GNUNET_OK !=
    2538          11 :         GNUNET_PQ_extract_result (result,
    2539             :                                   rs,
    2540             :                                   i))
    2541             :     {
    2542           0 :       GNUNET_break (0);
    2543           0 :       ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2544           0 :       return;
    2545             :     }
    2546             : 
    2547             :     struct FindRefundContext ictx;
    2548             :     enum GNUNET_DB_QueryStatus ires;
    2549          11 :     struct GNUNET_PQ_QueryParam params[] = {
    2550             :       GNUNET_PQ_query_param_auto_from_type (&coin_pub),
    2551             :       GNUNET_PQ_query_param_end
    2552             :     };
    2553             : 
    2554          11 :     GNUNET_assert (GNUNET_OK ==
    2555             :                    TALER_amount_get_zero (ctx->refund->currency,
    2556             :                                           &ictx.refunded_amount));
    2557          11 :     ictx.err = GNUNET_OK; /* no error so far */
    2558          11 :     ires = GNUNET_PQ_eval_prepared_multi_select (ctx->pg->conn,
    2559             :                                                  "find_refunds",
    2560             :                                                  params,
    2561             :                                                  &process_refund_cb,
    2562             :                                                  &ictx);
    2563          11 :     if ( (GNUNET_OK != ictx.err) ||
    2564             :          (GNUNET_DB_STATUS_HARD_ERROR == ires) )
    2565             :     {
    2566           0 :       GNUNET_break (0);
    2567           0 :       ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2568           0 :       return;
    2569             :     }
    2570          11 :     if (GNUNET_DB_STATUS_SOFT_ERROR == ires)
    2571             :     {
    2572           0 :       ctx->qs = GNUNET_DB_STATUS_SOFT_ERROR;
    2573           0 :       return;
    2574             :     }
    2575          11 :     deposit_refund[i] = ictx.refunded_amount;
    2576          11 :     deposit_amount_with_fee[i] = amount_with_fee;
    2577          11 :     deposit_coin_pubs[i] = coin_pub;
    2578          11 :     deposit_refund_fee[i] = refund_fee;
    2579          11 :     if (GNUNET_SYSERR ==
    2580          11 :         TALER_amount_add (&current_refund,
    2581             :                           &current_refund,
    2582             :                           &ictx.refunded_amount))
    2583             :     {
    2584           0 :       GNUNET_break (0);
    2585           0 :       ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2586           0 :       return;
    2587             :     }
    2588          11 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2589             :                 "Existing refund for coin %s is %s\n",
    2590             :                 TALER_B2S (&coin_pub),
    2591             :                 TALER_amount2s (&ictx.refunded_amount));
    2592             :   }
    2593             : 
    2594          12 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2595             :               "Total existing refund is %s\n",
    2596             :               TALER_amount2s (&current_refund));
    2597             : 
    2598             :   /* stop immediately if we are done */
    2599          12 :   if (0 >= TALER_amount_cmp (ctx->refund,
    2600             :                              &current_refund))
    2601             :   {
    2602           2 :     ctx->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    2603           2 :     return;
    2604             :   }
    2605             : 
    2606             :   /* Phase 2:  Try to increase current refund until it matches desired refund */
    2607             : 
    2608          11 :   for (unsigned int i=0;i<num_results; i++)
    2609             :   {
    2610             :     const struct TALER_Amount *increment;
    2611             :     struct TALER_Amount left;
    2612             :     struct TALER_Amount remaining_refund;
    2613             : 
    2614             :     /* How much of the coin is left after the existing refunds? */
    2615           9 :     if (GNUNET_SYSERR ==
    2616           9 :         TALER_amount_subtract (&left,
    2617           9 :                                &deposit_amount_with_fee[i],
    2618           9 :                                &deposit_refund[i]))
    2619             :     {
    2620           0 :       GNUNET_break (0);
    2621           0 :       ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2622           8 :       return;
    2623             :     }
    2624             : 
    2625           9 :     if ( (0 == left.value) &&
    2626           0 :          (0 == left.fraction) )
    2627             :     {
    2628             :       /* coin was fully refunded, move to next coin */
    2629           0 :       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2630             :                   "Coin %s fully refunded, moving to next coin\n",
    2631             :                   TALER_B2S (&deposit_coin_pubs[i]));
    2632           0 :       continue;
    2633             :     }
    2634             : 
    2635             :     /* How much of the refund is left? */
    2636           9 :     if (GNUNET_SYSERR ==
    2637           9 :         TALER_amount_subtract (&remaining_refund,
    2638             :                                ctx->refund,
    2639             :                                &current_refund))
    2640             :     {
    2641           0 :       GNUNET_break (0);
    2642           0 :       ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2643           0 :       return;
    2644             :     }
    2645             : 
    2646             :     /* By how much will we increase the refund for this coin? */
    2647           9 :     if (0 >= TALER_amount_cmp (&remaining_refund,
    2648             :                                &left))
    2649             :     {
    2650             :       /* remaining_refund <= left */
    2651           8 :       increment = &remaining_refund;
    2652             :     }
    2653             :     else
    2654             :     {
    2655           1 :       increment = &left;
    2656             :     }
    2657             : 
    2658           9 :     if (GNUNET_SYSERR ==
    2659           9 :         TALER_amount_add (&current_refund,
    2660             :                           &current_refund,
    2661             :                           increment))
    2662             :     {
    2663           0 :       GNUNET_break (0);
    2664           0 :       ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2665           0 :       return;
    2666             :     }
    2667             : 
    2668             :     /* actually run the refund */
    2669           9 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2670             :                 "Coin %s deposit amount is %s\n",
    2671             :                 TALER_B2S (&deposit_coin_pubs[i]),
    2672             :                 TALER_amount2s (&deposit_amount_with_fee[i]));
    2673           9 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2674             :                 "Coin %s refund will be incremented by %s\n",
    2675             :                 TALER_B2S (&deposit_coin_pubs[i]),
    2676             :                 TALER_amount2s (increment));
    2677             :     {
    2678             :       enum GNUNET_DB_QueryStatus qs;
    2679             : 
    2680           9 :       if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    2681          18 :           (qs = insert_refund (ctx->pg,
    2682             :                                ctx->merchant_pub,
    2683             :                                ctx->h_contract_terms,
    2684           9 :                                &deposit_coin_pubs[i],
    2685             :                                ctx->reason,
    2686             :                                increment,
    2687           9 :                                &deposit_refund_fee[i])))
    2688             :       {
    2689           0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2690           0 :         ctx->qs = qs;
    2691           0 :         return;
    2692             :       }
    2693             :     }
    2694             :     /* stop immediately if we are done */
    2695           9 :     if (0 == TALER_amount_cmp (ctx->refund,
    2696             :                                &current_refund))
    2697           8 :       return;
    2698             :   }
    2699             : 
    2700             :   /**
    2701             :    * We end up here if nto all of the refund has been covered.
    2702             :    * Although this should be checked as the business should never
    2703             :    * issue a refund bigger than the contract's actual price, we cannot
    2704             :    * rely upon the frontend being correct.
    2705             :    */
    2706           2 :   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2707             :               "The refund of %s is bigger than the order's value\n",
    2708             :               TALER_amount2s (ctx->refund));
    2709           2 :   ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2710             : }
    2711             : 
    2712             : 
    2713             : /**
    2714             :  * Function called when some backoffice staff decides to award or
    2715             :  * increase the refund on an existing contract.
    2716             :  *
    2717             :  * @param cls closure
    2718             :  * @param h_contract_terms
    2719             :  * @param merchant_pub merchant's instance public key
    2720             :  * @param refund maximum refund to return to the customer for this contract
    2721             :  * @param reason 0-terminated UTF-8 string giving the reason why the customer
    2722             :  *               got a refund (free form, business-specific)
    2723             :  * @return transaction status
    2724             :  *         #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the refund is accepted
    2725             :  *         #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the refund cannot be issued: this can happen for two
    2726             :  *               reasons: the issued refund is not greater of the previous refund,
    2727             :  *               or the coins don't have enough amount left to pay for this refund.
    2728             :  */
    2729             : static enum GNUNET_DB_QueryStatus
    2730          12 : postgres_increase_refund_for_contract (void *cls,
    2731             :                                        const struct GNUNET_HashCode *h_contract_terms,
    2732             :                                        const struct TALER_MerchantPublicKeyP *merchant_pub,
    2733             :                                        const struct TALER_Amount *refund,
    2734             :                                        const char *reason)
    2735             : {
    2736          12 :   struct PostgresClosure *pg = cls;
    2737             :   struct InsertRefundContext ctx;
    2738             :   enum GNUNET_DB_QueryStatus qs;
    2739          12 :   struct GNUNET_PQ_QueryParam params[] = {
    2740             :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    2741             :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
    2742             :     GNUNET_PQ_query_param_end
    2743             :   };
    2744             : 
    2745          12 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2746             :               "Asked to refund %s on contract %s\n",
    2747             :               TALER_amount2s (refund),
    2748             :               GNUNET_h2s (h_contract_terms));
    2749          12 :   if (GNUNET_OK !=
    2750          12 :       postgres_start (cls,
    2751             :                       "increase refund"))
    2752             :   {
    2753           0 :     GNUNET_break (0);
    2754           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    2755             :   }
    2756          12 :   ctx.pg = pg;
    2757          12 :   ctx.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    2758          12 :   ctx.refund = refund;
    2759          12 :   ctx.reason = reason;
    2760          12 :   ctx.h_contract_terms = h_contract_terms;
    2761          12 :   ctx.merchant_pub = merchant_pub;
    2762          12 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    2763             :                                              "find_deposits",
    2764             :                                              params,
    2765             :                                              &process_deposits_for_refund_cb,
    2766             :                                              &ctx);
    2767          12 :   switch (qs)
    2768             :   {
    2769             :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    2770           1 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2771             :                 "Unknown contract: %s (merchant_pub: %s), no refund possible\n",
    2772             :                 GNUNET_h2s (h_contract_terms),
    2773             :                 TALER_B2S (merchant_pub));
    2774           1 :     postgres_rollback (cls);
    2775           1 :     return qs;
    2776             :   case GNUNET_DB_STATUS_SOFT_ERROR:
    2777             :   case GNUNET_DB_STATUS_HARD_ERROR:
    2778           0 :     postgres_rollback (cls);
    2779           0 :     return qs;
    2780             :   default:
    2781             :     /* Got one or more deposits */
    2782          11 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ctx.qs)
    2783             :     {
    2784           3 :       postgres_rollback (cls);
    2785           3 :       return ctx.qs;
    2786             :     }
    2787           8 :     qs = postgres_commit (cls);
    2788           8 :     if (0 > qs)
    2789             :     {
    2790           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2791             :                   "Failed to commit transaction increasing refund\n");
    2792           0 :       return qs;
    2793             :     }
    2794           8 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2795             :                 "Committed refund transaction\n");
    2796           8 :     return ctx.qs;
    2797             :   }
    2798             : }
    2799             : 
    2800             : 
    2801             : /**
    2802             :  * Lookup proof information about a wire transfer.
    2803             :  *
    2804             :  * @param cls closure
    2805             :  * @param exchange_url from which exchange are we looking for proof
    2806             :  * @param wtid wire transfer identifier for the search
    2807             :  * @param cb function to call with proof data
    2808             :  * @param cb_cls closure for @a cb
    2809             :  * @return transaction status
    2810             :  */
    2811             : static enum GNUNET_DB_QueryStatus
    2812          16 : postgres_find_proof_by_wtid (void *cls,
    2813             :                              const char *exchange_url,
    2814             :                              const struct TALER_WireTransferIdentifierRawP *wtid,
    2815             :                              TALER_MERCHANTDB_ProofCallback cb,
    2816             :                              void *cb_cls)
    2817             : {
    2818          16 :   struct PostgresClosure *pg = cls;
    2819          16 :   struct GNUNET_PQ_QueryParam params[] = {
    2820             :     GNUNET_PQ_query_param_auto_from_type (wtid),
    2821             :     GNUNET_PQ_query_param_string (exchange_url),
    2822             :     GNUNET_PQ_query_param_end
    2823             :   };
    2824             :   json_t *proof;
    2825          16 :   struct GNUNET_PQ_ResultSpec rs[] = {
    2826             :     TALER_PQ_result_spec_json ("proof",
    2827             :                                &proof),
    2828             :     GNUNET_PQ_result_spec_end
    2829             :   };
    2830             :   enum GNUNET_DB_QueryStatus qs;
    2831             : 
    2832          16 :   check_connection (pg);
    2833          16 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    2834             :                                                  "find_proof_by_wtid",
    2835             :                                                  params,
    2836             :                                                  rs);
    2837          16 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    2838             :   {
    2839          10 :     cb (cb_cls,
    2840             :         proof);
    2841          10 :     GNUNET_PQ_cleanup_result (rs);
    2842             :   }
    2843          16 :   return qs;
    2844             : }
    2845             : 
    2846             : 
    2847             : /**
    2848             :  * Add @a credit to a reserve to be used for tipping.  Note that
    2849             :  * this function does not actually perform any wire transfers to
    2850             :  * credit the reserve, it merely tells the merchant backend that
    2851             :  * a reserve was topped up.  This has to happen before tips can be
    2852             :  * authorized.
    2853             :  *
    2854             :  * @param cls closure, typically a connection to the db
    2855             :  * @param reserve_priv which reserve is topped up or created
    2856             :  * @param credit_uuid unique identifier for the credit operation
    2857             :  * @param credit how much money was added to the reserve
    2858             :  * @param expiration when does the reserve expire?
    2859             :  * @return transaction status, usually
    2860             :  *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
    2861             :  *      #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
    2862             :  */
    2863             : static enum GNUNET_DB_QueryStatus
    2864          18 : postgres_enable_tip_reserve (void *cls,
    2865             :                              const struct TALER_ReservePrivateKeyP *reserve_priv,
    2866             :                              const struct GNUNET_HashCode *credit_uuid,
    2867             :                              const struct TALER_Amount *credit,
    2868             :                              struct GNUNET_TIME_Absolute expiration)
    2869             : {
    2870          18 :   struct PostgresClosure *pg = cls;
    2871             :   struct GNUNET_TIME_Absolute old_expiration;
    2872             :   struct TALER_Amount old_balance;
    2873             :   enum GNUNET_DB_QueryStatus qs;
    2874             :   struct GNUNET_TIME_Absolute new_expiration;
    2875             :   struct TALER_Amount new_balance;
    2876             :   unsigned int retries;
    2877             : 
    2878          18 :   retries = 0;
    2879          18 :   check_connection (pg);
    2880             :  RETRY:
    2881          18 :   if (MAX_RETRIES < ++retries)
    2882           0 :     return GNUNET_DB_STATUS_SOFT_ERROR;
    2883          18 :   if (GNUNET_OK !=
    2884          18 :       postgres_start (pg,
    2885             :                       "enable tip reserve"))
    2886             :   {
    2887           0 :     GNUNET_break (0);
    2888           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    2889             :   }
    2890             : 
    2891             :   /* ensure that credit_uuid is new/unique */
    2892             :   {
    2893          18 :     struct GNUNET_PQ_QueryParam params[] = {
    2894             :       GNUNET_PQ_query_param_auto_from_type (credit_uuid),
    2895             :       GNUNET_PQ_query_param_auto_from_type (reserve_priv),
    2896             :       GNUNET_PQ_query_param_end
    2897             :     };
    2898             : 
    2899          18 :     struct GNUNET_PQ_ResultSpec rs[] = {
    2900             :       GNUNET_PQ_result_spec_end
    2901             :     };
    2902          18 :     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    2903             :                                                    "lookup_tip_credit_uuid",
    2904             :                                                    params,
    2905             :                                                    rs);
    2906          18 :     if (0 > qs)
    2907             :     {
    2908           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2909           0 :       postgres_rollback (pg);
    2910           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    2911           0 :         goto RETRY;
    2912          10 :       return qs;
    2913             :     }
    2914          18 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
    2915             :     {
    2916             :       /* UUID already exists, we are done! */
    2917          10 :       postgres_rollback (pg);
    2918          10 :       return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    2919             :     }
    2920             :   }
    2921             : 
    2922             :   {
    2923             :     struct GNUNET_TIME_Absolute now;
    2924           8 :     struct GNUNET_PQ_QueryParam params[] = {
    2925             :       GNUNET_PQ_query_param_auto_from_type (reserve_priv),
    2926             :       GNUNET_PQ_query_param_auto_from_type (credit_uuid),
    2927             :       GNUNET_PQ_query_param_absolute_time (&now),
    2928             :       TALER_PQ_query_param_amount (credit),
    2929             :       GNUNET_PQ_query_param_end
    2930             :     };
    2931             : 
    2932           8 :     now = GNUNET_TIME_absolute_get ();
    2933           8 :     (void) GNUNET_TIME_round_abs (&now);
    2934           8 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    2935             :                                              "insert_tip_credit_uuid",
    2936             :                                              params);
    2937           8 :     if (0 > qs)
    2938             :     {
    2939           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2940           0 :       postgres_rollback (pg);
    2941           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    2942           0 :         goto RETRY;
    2943           0 :       return qs;
    2944             :     }
    2945             :   }
    2946             : 
    2947             :   /* Obtain existing reserve balance */
    2948             :   {
    2949           8 :     struct GNUNET_PQ_QueryParam params[] = {
    2950             :       GNUNET_PQ_query_param_auto_from_type (reserve_priv),
    2951             :       GNUNET_PQ_query_param_end
    2952             :     };
    2953           8 :     struct GNUNET_PQ_ResultSpec rs[] = {
    2954             :       GNUNET_PQ_result_spec_absolute_time ("expiration",
    2955             :                                            &old_expiration),
    2956             :       TALER_PQ_result_spec_amount ("balance",
    2957             :                                    &old_balance),
    2958             :       GNUNET_PQ_result_spec_end
    2959             :     };
    2960             : 
    2961           8 :     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    2962             :                                                    "lookup_tip_reserve_balance",
    2963             :                                                    params,
    2964             :                                                    rs);
    2965             :   }
    2966           8 :   if (0 > qs)
    2967             :   {
    2968           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2969           0 :     postgres_rollback (pg);
    2970           0 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    2971           0 :       goto RETRY;
    2972           0 :     return qs;
    2973             :   }
    2974          11 :   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
    2975           3 :        (GNUNET_TIME_absolute_get_remaining (old_expiration).rel_value_us > 0) )
    2976             :   {
    2977           2 :     new_expiration = GNUNET_TIME_absolute_max (old_expiration,
    2978             :                                                expiration);
    2979           4 :     if (GNUNET_OK !=
    2980           2 :         TALER_amount_add (&new_balance,
    2981             :                           credit,
    2982             :                           &old_balance))
    2983             :     {
    2984           0 :       GNUNET_break (0);
    2985           0 :       postgres_rollback (pg);
    2986           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    2987             :     }
    2988             :   }
    2989             :   else
    2990             :   {
    2991           6 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    2992             :     {
    2993           1 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2994             :                   "Old reserve balance of %s had expired at %s, not carrying it over!\n",
    2995             :                   TALER_amount2s (&old_balance),
    2996             :                   GNUNET_STRINGS_absolute_time_to_string (old_expiration));
    2997             :     }
    2998           6 :     new_expiration = expiration;
    2999           6 :     new_balance = *credit;
    3000             :   }
    3001             : 
    3002             :   {
    3003           8 :     struct GNUNET_PQ_QueryParam params[] = {
    3004             :       GNUNET_PQ_query_param_auto_from_type (reserve_priv),
    3005             :       GNUNET_PQ_query_param_absolute_time (&new_expiration),
    3006             :       TALER_PQ_query_param_amount (&new_balance),
    3007             :       GNUNET_PQ_query_param_end
    3008             :     };
    3009             :     const char *stmt;
    3010             : 
    3011           8 :     stmt = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    3012             :       ? "update_tip_reserve_balance"
    3013             :       : "insert_tip_reserve_balance";
    3014           8 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3015             :                                              stmt,
    3016             :                                              params);
    3017           8 :     if (0 > qs)
    3018             :     {
    3019           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    3020           0 :       postgres_rollback (pg);
    3021           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3022           0 :         goto RETRY;
    3023           0 :       return qs;
    3024             :     }
    3025             :   }
    3026           8 :   qs = postgres_commit (pg);
    3027           8 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    3028           8 :     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    3029           0 :   GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    3030           0 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3031           0 :     goto RETRY;
    3032           0 :   return qs;
    3033             : }
    3034             : 
    3035             : 
    3036             : /**
    3037             :  * Authorize a tip over @a amount from reserve @a reserve_priv.  Remember
    3038             :  * the authorization under @a tip_id for later, together with the
    3039             :  * @a justification.
    3040             :  *
    3041             :  * @param cls closure, typically a connection to the db
    3042             :  * @param justification why was the tip approved
    3043             :  * @param amount how high is the tip (with fees)
    3044             :  * @param reserve_priv which reserve is debited
    3045             :  * @param exchange_url which exchange manages the tip
    3046             :  * @param[out] expiration set to when the tip expires
    3047             :  * @param[out] tip_id set to the unique ID for the tip
    3048             :  * @return taler error code
    3049             :  *      #TALER_EC_TIP_AUTHORIZE_RESERVE_EXPIRED if the reserve is known but has expired
    3050             :  *      #TALER_EC_TIP_AUTHORIZE_RESERVE_UNKNOWN if the reserve is not known
    3051             :  *      #TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS if the reserve has insufficient funds left
    3052             :  *      #TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR on hard DB errors
    3053             :  *      #TALER_EC_TIP_AUTHORIZE_DB_SOFT_ERROR on soft DB errors (client should retry)
    3054             :  *      #TALER_EC_NONE upon success
    3055             :  */
    3056             : static enum TALER_ErrorCode
    3057          21 : postgres_authorize_tip (void *cls,
    3058             :                         const char *justification,
    3059             :                         const struct TALER_Amount *amount,
    3060             :                         const struct TALER_ReservePrivateKeyP *reserve_priv,
    3061             :                         const char *exchange_url,
    3062             :                         struct GNUNET_TIME_Absolute *expiration,
    3063             :                         struct GNUNET_HashCode *tip_id)
    3064             : {
    3065          21 :   struct PostgresClosure *pg = cls;
    3066          21 :   struct GNUNET_PQ_QueryParam params[] = {
    3067             :     GNUNET_PQ_query_param_auto_from_type (reserve_priv),
    3068             :     GNUNET_PQ_query_param_end
    3069             :   };
    3070             :   struct GNUNET_TIME_Absolute old_expiration;
    3071             :   struct TALER_Amount old_balance;
    3072          21 :   struct GNUNET_PQ_ResultSpec rs[] = {
    3073             :     GNUNET_PQ_result_spec_absolute_time ("expiration",
    3074             :                                          &old_expiration),
    3075             :     TALER_PQ_result_spec_amount ("balance",
    3076             :                                  &old_balance),
    3077             :     GNUNET_PQ_result_spec_end
    3078             :   };
    3079             :   enum GNUNET_DB_QueryStatus qs;
    3080             :   struct TALER_Amount new_balance;
    3081             :   unsigned int retries;
    3082             : 
    3083          21 :   retries = 0;
    3084          21 :   check_connection (pg);
    3085             :  RETRY:
    3086          21 :   if (MAX_RETRIES < ++retries)
    3087           0 :     return TALER_EC_TIP_AUTHORIZE_DB_SOFT_ERROR;
    3088          21 :   if (GNUNET_OK !=
    3089          21 :       postgres_start (pg,
    3090             :                       "authorize tip"))
    3091             :   {
    3092           0 :     GNUNET_break (0);
    3093           0 :     return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
    3094             :   }
    3095          21 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    3096             :                                                  "lookup_tip_reserve_balance",
    3097             :                                                  params,
    3098             :                                                  rs);
    3099          21 :   if (0 >= qs)
    3100             :   {
    3101             :     /* reserve unknown */
    3102           7 :     postgres_rollback (pg);
    3103           7 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3104           0 :       goto RETRY;
    3105           7 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    3106           7 :       return TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
    3107           0 :     return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
    3108             :   }
    3109          14 :   if (0 == GNUNET_TIME_absolute_get_remaining (old_expiration).rel_value_us)
    3110             :   {
    3111             :     /* reserve expired, can't be used */
    3112           1 :     postgres_rollback (pg);
    3113           1 :     return TALER_EC_TIP_AUTHORIZE_RESERVE_EXPIRED;
    3114             :   }
    3115          13 :   if (GNUNET_SYSERR ==
    3116          13 :       TALER_amount_subtract (&new_balance,
    3117             :                              &old_balance,
    3118             :                              amount))
    3119             :   {
    3120             :     /* insufficient funds left in reserve */
    3121           5 :     postgres_rollback (pg);
    3122           5 :     return TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
    3123             :   }
    3124             :   /* Update reserve balance */
    3125             :   {
    3126           8 :     struct GNUNET_PQ_QueryParam params[] = {
    3127             :       GNUNET_PQ_query_param_auto_from_type (reserve_priv),
    3128             :       GNUNET_PQ_query_param_absolute_time (&old_expiration),
    3129             :       TALER_PQ_query_param_amount (&new_balance),
    3130             :       GNUNET_PQ_query_param_end
    3131             :     };
    3132             : 
    3133           8 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3134             :                                              "update_tip_reserve_balance",
    3135             :                                              params);
    3136           8 :     if (0 > qs)
    3137             :     {
    3138           0 :       postgres_rollback (pg);
    3139           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3140           0 :         goto RETRY;
    3141           0 :       return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
    3142             :     }
    3143             :   }
    3144             :   /* Generate and store tip ID */
    3145           8 :   *expiration = old_expiration;
    3146           8 :   GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG,
    3147             :                                     tip_id);
    3148             :   {
    3149           8 :     struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
    3150           8 :     struct GNUNET_PQ_QueryParam params[] = {
    3151             :       GNUNET_PQ_query_param_auto_from_type (reserve_priv),
    3152             :       GNUNET_PQ_query_param_auto_from_type (tip_id),
    3153             :       GNUNET_PQ_query_param_string (exchange_url),
    3154             :       GNUNET_PQ_query_param_string (justification),
    3155             :       GNUNET_PQ_query_param_absolute_time (&now),
    3156             :       TALER_PQ_query_param_amount (amount), /* overall amount */
    3157             :       TALER_PQ_query_param_amount (amount), /* amount left */
    3158             :       GNUNET_PQ_query_param_end
    3159             :     };
    3160             : 
    3161           8 :     (void) GNUNET_TIME_round_abs (&now);
    3162           8 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3163             :                                              "insert_tip_justification",
    3164             :                                              params);
    3165           8 :     if (0 > qs)
    3166             :     {
    3167           0 :       postgres_rollback (pg);
    3168           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3169           0 :         goto RETRY;
    3170           0 :       return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
    3171             :     }
    3172             :   }
    3173           8 :   qs = postgres_commit (pg);
    3174           8 :   if (0 <= qs)
    3175           8 :     return TALER_EC_NONE; /* success! */
    3176           0 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3177           0 :     goto RETRY;
    3178           0 :   return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
    3179             : }
    3180             : 
    3181             : 
    3182             : /**
    3183             :  * Find out tip authorization details associated with @a tip_id
    3184             :  *
    3185             :  * @param cls closure, typically a connection to the d
    3186             :  * @param tip_id the unique ID for the tip
    3187             :  * @param[out] exchange_url set to the URL of the exchange (unless NULL)
    3188             :  * @param[out] amount set to the authorized amount (unless NULL)
    3189             :  * @param[out] timestamp set to the timestamp of the tip authorization (unless NULL)
    3190             :  * @return transaction status, usually
    3191             :  *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
    3192             :  *      #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
    3193             :  */
    3194             : static enum GNUNET_DB_QueryStatus
    3195          13 : postgres_lookup_tip_by_id (void *cls,
    3196             :                            const struct GNUNET_HashCode *tip_id,
    3197             :                            char **exchange_url,
    3198             :                            struct TALER_Amount *amount,
    3199             :                            struct GNUNET_TIME_Absolute *timestamp)
    3200             : {
    3201             :   char *res_exchange_url;
    3202             :   struct TALER_Amount res_amount;
    3203             :   struct GNUNET_TIME_Absolute res_timestamp;
    3204             : 
    3205          13 :   struct PostgresClosure *pg = cls;
    3206          13 :   struct GNUNET_PQ_QueryParam params[] = {
    3207             :     GNUNET_PQ_query_param_auto_from_type (tip_id),
    3208             :     GNUNET_PQ_query_param_end
    3209             :   };
    3210          13 :   struct GNUNET_PQ_ResultSpec rs[] = {
    3211             :     GNUNET_PQ_result_spec_string ("exchange_url",
    3212             :                                   &res_exchange_url),
    3213             :     GNUNET_PQ_result_spec_absolute_time ("timestamp",
    3214             :                                          &res_timestamp),
    3215             :     TALER_PQ_result_spec_amount ("amount",
    3216             :                                   &res_amount),
    3217             :     GNUNET_PQ_result_spec_end
    3218             :   };
    3219             :   enum GNUNET_DB_QueryStatus qs;
    3220             : 
    3221          13 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    3222             :                                                  "find_tip_by_id",
    3223             :                                                  params,
    3224             :                                                  rs);
    3225          13 :   if (0 >= qs)
    3226             :   {
    3227           1 :     if (NULL != exchange_url)
    3228           1 :       *exchange_url = NULL;
    3229           1 :     return qs;
    3230             :   }
    3231          12 :   if (NULL != exchange_url)
    3232          12 :     *exchange_url = strdup (res_exchange_url);
    3233          12 :   if (NULL != amount)
    3234           0 :     *amount = res_amount;
    3235          12 :   if (NULL != timestamp)
    3236           0 :     *timestamp = res_timestamp;
    3237          12 :   GNUNET_PQ_cleanup_result (rs);
    3238          12 :   return qs;
    3239             : }
    3240             : 
    3241             : 
    3242             : /**
    3243             :  * Pickup a tip over @a amount using pickup id @a pickup_id.
    3244             :  *
    3245             :  * @param cls closure, typically a connection to the db
    3246             :  * @param amount how high is the amount picked up (with fees)
    3247             :  * @param tip_id the unique ID from the tip authorization
    3248             :  * @param pickup_id the unique ID identifying the pick up operation
    3249             :  *        (to allow replays, hash over the coin envelope and denomination key)
    3250             :  * @param[out] reserve_priv which reserve key to use to sign
    3251             :  * @return taler error code
    3252             :  *      #TALER_EC_TIP_PICKUP_ID_UNKNOWN if @a tip_id is unknown
    3253             :  *      #TALER_EC_TIP_PICKUP_NO_FUNDS if @a tip_id has insufficient funds left
    3254             :  *      #TALER_EC_TIP_PICKUP_DB_ERROR_HARD on hard database errors
    3255             :  *      #TALER_EC_TIP_PICKUP_AMOUNT_CHANGED if @a amount is different for known @a pickup_id
    3256             :  *      #TALER_EC_TIP_PICKUP_DB_ERROR_SOFT on soft database errors (client should retry)
    3257             :  *      #TALER_EC_NONE upon success (@a reserve_priv was set)
    3258             :  */
    3259             : static enum TALER_ErrorCode
    3260          15 : postgres_pickup_tip (void *cls,
    3261             :                      const struct TALER_Amount *amount,
    3262             :                      const struct GNUNET_HashCode *tip_id,
    3263             :                      const struct GNUNET_HashCode *pickup_id,
    3264             :                      struct TALER_ReservePrivateKeyP *reserve_priv)
    3265             : {
    3266          15 :   struct PostgresClosure *pg = cls;
    3267             :   struct TALER_Amount left_amount;
    3268          15 :   struct GNUNET_PQ_QueryParam params[] = {
    3269             :     GNUNET_PQ_query_param_auto_from_type (tip_id),
    3270             :     GNUNET_PQ_query_param_end
    3271             :   };
    3272          15 :   struct GNUNET_PQ_ResultSpec rs[] = {
    3273             :     GNUNET_PQ_result_spec_auto_from_type ("reserve_priv",
    3274             :                                           reserve_priv),
    3275             :     TALER_PQ_result_spec_amount ("left",
    3276             :                                  &left_amount),
    3277             :     GNUNET_PQ_result_spec_end
    3278             :   };
    3279             :   enum GNUNET_DB_QueryStatus qs;
    3280             :   unsigned int retries;
    3281             : 
    3282          15 :   retries = 0;
    3283          15 :   check_connection (pg);
    3284             :  RETRY:
    3285          15 :   if (MAX_RETRIES < ++retries)
    3286           0 :     return TALER_EC_TIP_PICKUP_DB_ERROR_SOFT;
    3287          15 :   if (GNUNET_OK !=
    3288          15 :       postgres_start (pg,
    3289             :                       "pickup tip"))
    3290             :   {
    3291           0 :     GNUNET_break (0);
    3292           0 :     return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
    3293             :   }
    3294          15 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    3295             :                                                  "lookup_reserve_by_tip_id",
    3296             :                                                  params,
    3297             :                                                  rs);
    3298          15 :   if (0 >= qs)
    3299             :   {
    3300             :     /* tip ID unknown */
    3301           1 :     memset (reserve_priv,
    3302             :             0,
    3303             :             sizeof (*reserve_priv));
    3304           1 :     postgres_rollback (pg);
    3305           1 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    3306           1 :       return TALER_EC_TIP_PICKUP_TIP_ID_UNKNOWN;
    3307           0 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3308           0 :       goto RETRY;
    3309           0 :     return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
    3310             :   }
    3311             : 
    3312             :   /* Check if pickup_id already exists */
    3313             :   {
    3314             :     struct TALER_Amount existing_amount;
    3315          14 :     struct GNUNET_PQ_QueryParam params[] = {
    3316             :       GNUNET_PQ_query_param_auto_from_type (pickup_id),
    3317             :       GNUNET_PQ_query_param_auto_from_type (tip_id),
    3318             :       GNUNET_PQ_query_param_end
    3319             :     };
    3320          14 :     struct GNUNET_PQ_ResultSpec rs[] = {
    3321             :       TALER_PQ_result_spec_amount ("amount",
    3322             :                                    &existing_amount),
    3323             :       GNUNET_PQ_result_spec_end
    3324             :     };
    3325             : 
    3326          14 :     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    3327             :                                                    "lookup_amount_by_pickup",
    3328             :                                                    params,
    3329             :                                                    rs);
    3330          14 :     if (0 > qs)
    3331             :     {
    3332             :       /* DB error */
    3333           0 :       memset (reserve_priv,
    3334             :               0,
    3335             :               sizeof (*reserve_priv));
    3336           0 :       postgres_rollback (pg);
    3337           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3338           0 :         goto RETRY;
    3339           2 :       return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
    3340             :     }
    3341          14 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    3342             :     {
    3343           2 :       if (0 !=
    3344           2 :           TALER_amount_cmp (&existing_amount,
    3345             :                             amount))
    3346             :       {
    3347           0 :         GNUNET_break_op (0);
    3348           0 :         postgres_rollback (pg);
    3349           0 :         return TALER_EC_TIP_PICKUP_AMOUNT_CHANGED;
    3350             :       }
    3351           2 :       postgres_commit (pg);
    3352           2 :       return TALER_EC_NONE; /* we are done! */
    3353             :     }
    3354             :   }
    3355             : 
    3356             :   /* Calculate new balance */
    3357             :   {
    3358             :     struct TALER_Amount new_left;
    3359             : 
    3360          12 :     if (GNUNET_SYSERR ==
    3361          12 :         TALER_amount_subtract (&new_left,
    3362             :                                &left_amount,
    3363             :                                amount))
    3364             :     {
    3365             :       /* attempt to take more tips than the tipping amount */
    3366           4 :       GNUNET_break_op (0);
    3367           4 :       memset (reserve_priv,
    3368             :               0,
    3369             :               sizeof (*reserve_priv));
    3370           4 :       postgres_rollback (pg);
    3371           8 :       return TALER_EC_TIP_PICKUP_NO_FUNDS;
    3372             :     }
    3373             : 
    3374             :     /* Update DB: update balance */
    3375             :     {
    3376           8 :       struct GNUNET_PQ_QueryParam params[] = {
    3377             :         GNUNET_PQ_query_param_auto_from_type (tip_id),
    3378             :         TALER_PQ_query_param_amount (&new_left),
    3379             :         GNUNET_PQ_query_param_end
    3380             :       };
    3381             : 
    3382           8 :       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3383             :                                                "update_tip_balance",
    3384             :                                                params);
    3385           8 :       if (0 > qs)
    3386             :       {
    3387           0 :         postgres_rollback (pg);
    3388           0 :         memset (reserve_priv,
    3389             :                 0,
    3390             :                 sizeof (*reserve_priv));
    3391           0 :         if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3392           0 :           goto RETRY;
    3393           0 :         return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
    3394             :       }
    3395             :     }
    3396             : 
    3397             :     /* Update DB: remember pickup_id */
    3398             :     {
    3399           8 :       struct GNUNET_PQ_QueryParam params[] = {
    3400             :         GNUNET_PQ_query_param_auto_from_type (tip_id),
    3401             :         GNUNET_PQ_query_param_auto_from_type (pickup_id),
    3402             :         TALER_PQ_query_param_amount (amount),
    3403             :         GNUNET_PQ_query_param_end
    3404             :       };
    3405             : 
    3406           8 :       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3407             :                                                "insert_pickup_id",
    3408             :                                                params);
    3409           8 :       if (0 > qs)
    3410             :       {
    3411           0 :         postgres_rollback (pg);
    3412           0 :         memset (reserve_priv,
    3413             :                 0,
    3414             :                 sizeof (*reserve_priv));
    3415           0 :         if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3416           0 :           goto RETRY;
    3417           0 :         return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
    3418             :       }
    3419             :     }
    3420             :   }
    3421           8 :   qs = postgres_commit (pg);
    3422           8 :   if (0 <= qs)
    3423           8 :     return TALER_EC_NONE; /* success  */
    3424           0 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3425           0 :     goto RETRY;
    3426           0 :   return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
    3427             : }
    3428             : 
    3429             : 
    3430             : /**
    3431             :  * Initialize Postgres database subsystem.
    3432             :  *
    3433             :  * @param cls a configuration instance
    3434             :  * @return NULL on error, otherwise a `struct TALER_MERCHANTDB_Plugin`
    3435             :  */
    3436             : void *
    3437           5 : libtaler_plugin_merchantdb_postgres_init (void *cls)
    3438             : {
    3439           5 :   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
    3440             :   struct PostgresClosure *pg;
    3441             :   struct TALER_MERCHANTDB_Plugin *plugin;
    3442             :   const char *ec;
    3443             : 
    3444           5 :   pg = GNUNET_new (struct PostgresClosure);
    3445           5 :   ec = getenv ("TALER_MERCHANTDB_POSTGRES_CONFIG");
    3446           5 :   if (NULL != ec)
    3447             :   {
    3448           5 :     GNUNET_CONFIGURATION_set_value_string (cfg,
    3449             :                                            "merchantdb-postgres",
    3450             :                                            "CONFIG",
    3451             :                                            ec);
    3452             :   }
    3453             :   else
    3454             :   {
    3455           0 :     if (GNUNET_OK !=
    3456           0 :         GNUNET_CONFIGURATION_have_value (cfg,
    3457             :                                          "merchantdb-postgres",
    3458             :                                          "CONFIG"))
    3459             :     {
    3460           0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    3461             :                                  "merchantdb-postgres",
    3462             :                                  "CONFIG");
    3463           0 :       return NULL;
    3464             :     }
    3465             :   }
    3466           5 :   pg->cfg = cfg;
    3467           5 :   pg->conn = GNUNET_PQ_connect_with_cfg (cfg,
    3468             :                                          "merchantdb-postgres");
    3469           5 :   if (NULL == pg->conn)
    3470             :   {
    3471           0 :     GNUNET_free (pg);
    3472           0 :     return NULL;
    3473             :   }
    3474           5 :   plugin = GNUNET_new (struct TALER_MERCHANTDB_Plugin);
    3475           5 :   plugin->cls = pg;
    3476           5 :   plugin->drop_tables = &postgres_drop_tables;
    3477           5 :   plugin->initialize = &postgres_initialize;
    3478           5 :   plugin->store_deposit = &postgres_store_deposit;
    3479           5 :   plugin->store_coin_to_transfer = &postgres_store_coin_to_transfer;
    3480           5 :   plugin->store_transfer_to_proof = &postgres_store_transfer_to_proof;
    3481           5 :   plugin->store_wire_fee_by_exchange = &postgres_store_wire_fee_by_exchange;
    3482           5 :   plugin->find_payments_by_hash_and_coin = &postgres_find_payments_by_hash_and_coin;
    3483           5 :   plugin->find_payments = &postgres_find_payments;
    3484           5 :   plugin->find_transfers_by_hash = &postgres_find_transfers_by_hash;
    3485           5 :   plugin->find_deposits_by_wtid = &postgres_find_deposits_by_wtid;
    3486           5 :   plugin->find_proof_by_wtid = &postgres_find_proof_by_wtid;
    3487           5 :   plugin->insert_contract_terms = &postgres_insert_contract_terms;
    3488           5 :   plugin->insert_order = &postgres_insert_order;
    3489           5 :   plugin->find_order = &postgres_find_order;
    3490           5 :   plugin->find_contract_terms = &postgres_find_contract_terms;
    3491           5 :   plugin->find_contract_terms_history = &postgres_find_contract_terms_history;
    3492           5 :   plugin->find_contract_terms_by_date = &postgres_find_contract_terms_by_date;
    3493           5 :   plugin->get_authorized_tip_amount = &postgres_get_authorized_tip_amount;
    3494           5 :   plugin->find_contract_terms_by_date_and_range = &postgres_find_contract_terms_by_date_and_range;
    3495           5 :   plugin->find_contract_terms_from_hash = &postgres_find_contract_terms_from_hash;
    3496           5 :   plugin->find_paid_contract_terms_from_hash = &postgres_find_paid_contract_terms_from_hash;
    3497           5 :   plugin->get_refunds_from_contract_terms_hash = &postgres_get_refunds_from_contract_terms_hash;
    3498           5 :   plugin->lookup_wire_fee = &postgres_lookup_wire_fee;
    3499           5 :   plugin->increase_refund_for_contract = &postgres_increase_refund_for_contract;
    3500           5 :   plugin->mark_proposal_paid = &postgres_mark_proposal_paid;
    3501           5 :   plugin->enable_tip_reserve = &postgres_enable_tip_reserve;
    3502           5 :   plugin->authorize_tip = &postgres_authorize_tip;
    3503           5 :   plugin->lookup_tip_by_id = &postgres_lookup_tip_by_id;
    3504           5 :   plugin->pickup_tip = &postgres_pickup_tip;
    3505           5 :   plugin->start = postgres_start;
    3506           5 :   plugin->commit = postgres_commit;
    3507           5 :   plugin->preflight = postgres_preflight;
    3508           5 :   plugin->rollback = postgres_rollback;
    3509             : 
    3510           5 :   return plugin;
    3511             : }
    3512             : 
    3513             : 
    3514             : /**
    3515             :  * Shutdown Postgres database subsystem.
    3516             :  *
    3517             :  * @param cls a `struct TALER_MERCHANTDB_Plugin`
    3518             :  * @return NULL (always)
    3519             :  */
    3520             : void *
    3521           5 : libtaler_plugin_merchantdb_postgres_done (void *cls)
    3522             : {
    3523           5 :   struct TALER_MERCHANTDB_Plugin *plugin = cls;
    3524           5 :   struct PostgresClosure *pg = plugin->cls;
    3525             : 
    3526           5 :   PQfinish (pg->conn);
    3527           5 :   GNUNET_free (pg);
    3528           5 :   GNUNET_free (plugin);
    3529           5 :   return NULL;
    3530             : }
    3531             : 
    3532             : /* end of plugin_merchantdb_postgres.c */

Generated by: LCOV version 1.13