LCOV - code coverage report
Current view: top level - exchangedb - pg_get_coin_transactions.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 188 376 50.0 %
Date: 2025-06-22 12:09:43 Functions: 6 11 54.5 %

          Line data    Source code
       1             : /*
       2             :    This file is part of TALER
       3             :    Copyright (C) 2022-2023 Taler Systems SA
       4             : 
       5             :    TALER is free software; you can redistribute it and/or modify it under the
       6             :    terms of the GNU General Public License as published by the Free Software
       7             :    Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :    TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :    A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12             : 
      13             :    You should have received a copy of the GNU General Public License along with
      14             :    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             :  */
      16             : /**
      17             :  * @file pg_get_coin_transactions.c
      18             :  * @brief Low-level (statement-level) Postgres database access for the exchange
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "taler/platform.h"
      22             : #include "taler/taler_error_codes.h"
      23             : #include "taler/taler_dbevents.h"
      24             : #include "taler/taler_exchangedb_plugin.h"
      25             : #include "taler/taler_pq_lib.h"
      26             : #include "pg_get_coin_transactions.h"
      27             : #include "pg_helper.h"
      28             : #include "pg_start_read_committed.h"
      29             : #include "pg_commit.h"
      30             : #include "pg_rollback.h"
      31             : #include "plugin_exchangedb_common.h"
      32             : 
      33             : /**
      34             :  * How often do we re-try when encountering DB serialization issues?
      35             :  * (We are read-only, so can only happen due to concurrent insert,
      36             :  * which should be very rare.)
      37             :  */
      38             : #define RETRIES 3
      39             : 
      40             : /**
      41             :  * Closure for callbacks called from #TEH_PG_get_coin_transactions()
      42             :  */
      43             : struct CoinHistoryContext
      44             : {
      45             :   /**
      46             :    * Head of the coin's history list.
      47             :    */
      48             :   struct TALER_EXCHANGEDB_TransactionList *head;
      49             : 
      50             :   /**
      51             :    * Public key of the coin we are building the history for.
      52             :    */
      53             :   const struct TALER_CoinSpendPublicKeyP *coin_pub;
      54             : 
      55             :   /**
      56             :    * Plugin context.
      57             :    */
      58             :   struct PostgresClosure *pg;
      59             : 
      60             :   /**
      61             :    * Our current offset in the coin history.
      62             :    */
      63             :   uint64_t chid;
      64             : 
      65             :   /**
      66             :    * Set to 'true' if the transaction failed.
      67             :    */
      68             :   bool failed;
      69             : 
      70             : };
      71             : 
      72             : 
      73             : /**
      74             :  * Function to be called with the results of a SELECT statement
      75             :  * that has returned @a num_results results.
      76             :  *
      77             :  * @param cls closure of type `struct CoinHistoryContext`
      78             :  * @param result the postgres result
      79             :  * @param num_results the number of results in @a result
      80             :  */
      81             : static void
      82         146 : add_coin_deposit (void *cls,
      83             :                   PGresult *result,
      84             :                   unsigned int num_results)
      85             : {
      86         146 :   struct CoinHistoryContext *chc = cls;
      87         146 :   struct PostgresClosure *pg = chc->pg;
      88             : 
      89         289 :   for (unsigned int i = 0; i < num_results; i++)
      90             :   {
      91             :     struct TALER_EXCHANGEDB_DepositListEntry *deposit;
      92             :     struct TALER_EXCHANGEDB_TransactionList *tl;
      93             :     uint64_t serial_id;
      94             : 
      95         143 :     deposit = GNUNET_new (struct TALER_EXCHANGEDB_DepositListEntry);
      96             :     {
      97         143 :       struct GNUNET_PQ_ResultSpec rs[] = {
      98         143 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
      99             :                                      &deposit->amount_with_fee),
     100         143 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
     101             :                                      &deposit->deposit_fee),
     102         143 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
     103             :                                               &deposit->h_denom_pub),
     104         143 :         GNUNET_PQ_result_spec_allow_null (
     105         143 :           GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
     106             :                                                 &deposit->h_age_commitment),
     107             :           &deposit->no_age_commitment),
     108         143 :         GNUNET_PQ_result_spec_allow_null (
     109         143 :           GNUNET_PQ_result_spec_auto_from_type ("wallet_data_hash",
     110             :                                                 &deposit->wallet_data_hash),
     111             :           &deposit->no_wallet_data_hash),
     112         143 :         GNUNET_PQ_result_spec_timestamp ("wallet_timestamp",
     113             :                                          &deposit->timestamp),
     114         143 :         GNUNET_PQ_result_spec_timestamp ("refund_deadline",
     115             :                                          &deposit->refund_deadline),
     116         143 :         GNUNET_PQ_result_spec_timestamp ("wire_deadline",
     117             :                                          &deposit->wire_deadline),
     118         143 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
     119             :                                               &deposit->merchant_pub),
     120         143 :         GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
     121             :                                               &deposit->h_contract_terms),
     122         143 :         GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
     123             :                                               &deposit->wire_salt),
     124         143 :         GNUNET_PQ_result_spec_string ("payto_uri",
     125             :                                       &deposit->receiver_wire_account.full_payto
     126             :                                       ),
     127         143 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
     128             :                                               &deposit->csig),
     129         143 :         GNUNET_PQ_result_spec_uint64 ("coin_deposit_serial_id",
     130             :                                       &serial_id),
     131         143 :         GNUNET_PQ_result_spec_auto_from_type ("done",
     132             :                                               &deposit->done),
     133             :         GNUNET_PQ_result_spec_end
     134             :       };
     135             : 
     136         143 :       if (GNUNET_OK !=
     137         143 :           GNUNET_PQ_extract_result (result,
     138             :                                     rs,
     139             :                                     i))
     140             :       {
     141           0 :         GNUNET_break (0);
     142           0 :         GNUNET_free (deposit);
     143           0 :         chc->failed = true;
     144           0 :         return;
     145             :       }
     146             :     }
     147         143 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     148         143 :     tl->next = chc->head;
     149         143 :     tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
     150         143 :     tl->details.deposit = deposit;
     151         143 :     tl->serial_id = serial_id;
     152         143 :     tl->coin_history_id = chc->chid;
     153         143 :     chc->head = tl;
     154             :   }
     155             : }
     156             : 
     157             : 
     158             : /**
     159             :  * Function to be called with the results of a SELECT statement
     160             :  * that has returned @a num_results results.
     161             :  *
     162             :  * @param cls closure of type `struct CoinHistoryContext`
     163             :  * @param result the postgres result
     164             :  * @param num_results the number of results in @a result
     165             :  */
     166             : static void
     167           3 : add_coin_purse_deposit (void *cls,
     168             :                         PGresult *result,
     169             :                         unsigned int num_results)
     170             : {
     171           3 :   struct CoinHistoryContext *chc = cls;
     172           3 :   struct PostgresClosure *pg = chc->pg;
     173             : 
     174           6 :   for (unsigned int i = 0; i < num_results; i++)
     175             :   {
     176             :     struct TALER_EXCHANGEDB_PurseDepositListEntry *deposit;
     177             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     178             :     uint64_t serial_id;
     179             : 
     180           3 :     deposit = GNUNET_new (struct TALER_EXCHANGEDB_PurseDepositListEntry);
     181             :     {
     182             :       bool not_finished;
     183           3 :       struct GNUNET_PQ_ResultSpec rs[] = {
     184           3 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
     185             :                                      &deposit->amount),
     186           3 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
     187             :                                      &deposit->deposit_fee),
     188           3 :         GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
     189             :                                               &deposit->purse_pub),
     190           3 :         GNUNET_PQ_result_spec_uint64 ("purse_deposit_serial_id",
     191             :                                       &serial_id),
     192           3 :         GNUNET_PQ_result_spec_allow_null (
     193             :           GNUNET_PQ_result_spec_string ("partner_base_url",
     194             :                                         &deposit->exchange_base_url),
     195             :           NULL),
     196           3 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
     197             :                                               &deposit->coin_sig),
     198           3 :         GNUNET_PQ_result_spec_allow_null (
     199           3 :           GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
     200             :                                                 &deposit->h_age_commitment),
     201             :           &deposit->no_age_commitment),
     202           3 :         GNUNET_PQ_result_spec_allow_null (
     203             :           GNUNET_PQ_result_spec_bool ("refunded",
     204             :                                       &deposit->refunded),
     205             :           &not_finished),
     206           3 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
     207             :                                               &deposit->h_denom_pub),
     208             :         GNUNET_PQ_result_spec_end
     209             :       };
     210             : 
     211           3 :       if (GNUNET_OK !=
     212           3 :           GNUNET_PQ_extract_result (result,
     213             :                                     rs,
     214             :                                     i))
     215             :       {
     216           0 :         GNUNET_break (0);
     217           0 :         GNUNET_free (deposit);
     218           0 :         chc->failed = true;
     219           0 :         return;
     220             :       }
     221           3 :       if (not_finished)
     222           1 :         deposit->refunded = false;
     223             :       /* double-check for all-zeros age commitment */
     224           3 :       if (! deposit->no_age_commitment)
     225             :         deposit->no_age_commitment
     226           0 :           = GNUNET_is_zero (&deposit->h_age_commitment);
     227             :     }
     228           3 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     229           3 :     tl->next = chc->head;
     230           3 :     tl->type = TALER_EXCHANGEDB_TT_PURSE_DEPOSIT;
     231           3 :     tl->details.purse_deposit = deposit;
     232           3 :     tl->serial_id = serial_id;
     233           3 :     tl->coin_history_id = chc->chid;
     234           3 :     chc->head = tl;
     235             :   }
     236             : }
     237             : 
     238             : 
     239             : /**
     240             :  * Function to be called with the results of a SELECT statement
     241             :  * that has returned @a num_results results.
     242             :  *
     243             :  * @param cls closure of type `struct CoinHistoryContext`
     244             :  * @param result the postgres result
     245             :  * @param num_results the number of results in @a result
     246             :  */
     247             : static void
     248         192 : add_coin_melt (void *cls,
     249             :                PGresult *result,
     250             :                unsigned int num_results)
     251             : {
     252         192 :   struct CoinHistoryContext *chc = cls;
     253         192 :   struct PostgresClosure *pg = chc->pg;
     254             : 
     255         384 :   for (unsigned int i = 0; i<num_results; i++)
     256             :   {
     257             :     struct TALER_EXCHANGEDB_MeltListEntry *melt;
     258             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     259             :     uint64_t serial_id;
     260             : 
     261         192 :     melt = GNUNET_new (struct TALER_EXCHANGEDB_MeltListEntry);
     262             :     {
     263         192 :       struct GNUNET_PQ_ResultSpec rs[] = {
     264         192 :         GNUNET_PQ_result_spec_auto_from_type ("rc",
     265             :                                               &melt->rc),
     266             :         /* oldcoin_index not needed */
     267         192 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
     268             :                                               &melt->h_denom_pub),
     269         192 :         GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
     270             :                                               &melt->coin_sig),
     271         192 :         GNUNET_PQ_result_spec_auto_from_type ("refresh_seed",
     272             :                                               &melt->refresh_seed),
     273         192 :         GNUNET_PQ_result_spec_allow_null (
     274         192 :           GNUNET_PQ_result_spec_auto_from_type ("blinding_seed",
     275             :                                                 &melt->blinding_seed),
     276             :           &melt->no_blinding_seed),
     277         192 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
     278             :                                      &melt->amount_with_fee),
     279         192 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
     280             :                                      &melt->melt_fee),
     281         192 :         GNUNET_PQ_result_spec_allow_null (
     282         192 :           GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
     283             :                                                 &melt->h_age_commitment),
     284             :           &melt->no_age_commitment),
     285         192 :         GNUNET_PQ_result_spec_uint64 ("refresh_id",
     286             :                                       &serial_id),
     287             :         GNUNET_PQ_result_spec_end
     288             :       };
     289             : 
     290         192 :       if (GNUNET_OK !=
     291         192 :           GNUNET_PQ_extract_result (result,
     292             :                                     rs,
     293             :                                     i))
     294             :       {
     295           0 :         GNUNET_break (0);
     296           0 :         GNUNET_free (melt);
     297           0 :         chc->failed = true;
     298           0 :         return;
     299             :       }
     300             :     }
     301         192 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     302         192 :     tl->next = chc->head;
     303         192 :     tl->type = TALER_EXCHANGEDB_TT_MELT;
     304         192 :     tl->details.melt = melt;
     305         192 :     tl->serial_id = serial_id;
     306         192 :     tl->coin_history_id = chc->chid;
     307         192 :     chc->head = tl;
     308             :   }
     309             : }
     310             : 
     311             : 
     312             : /**
     313             :  * Function to be called with the results of a SELECT statement
     314             :  * that has returned @a num_results results.
     315             :  *
     316             :  * @param cls closure of type `struct CoinHistoryContext`
     317             :  * @param result the postgres result
     318             :  * @param num_results the number of results in @a result
     319             :  */
     320             : static void
     321          48 : add_coin_refund (void *cls,
     322             :                  PGresult *result,
     323             :                  unsigned int num_results)
     324             : {
     325          48 :   struct CoinHistoryContext *chc = cls;
     326          48 :   struct PostgresClosure *pg = chc->pg;
     327             : 
     328          93 :   for (unsigned int i = 0; i<num_results; i++)
     329             :   {
     330             :     struct TALER_EXCHANGEDB_RefundListEntry *refund;
     331             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     332             :     uint64_t serial_id;
     333             : 
     334          45 :     refund = GNUNET_new (struct TALER_EXCHANGEDB_RefundListEntry);
     335             :     {
     336          45 :       struct GNUNET_PQ_ResultSpec rs[] = {
     337          45 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
     338             :                                               &refund->merchant_pub),
     339          45 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
     340             :                                               &refund->merchant_sig),
     341          45 :         GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
     342             :                                               &refund->h_contract_terms),
     343          45 :         GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
     344             :                                       &refund->rtransaction_id),
     345          45 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
     346             :                                      &refund->refund_amount),
     347          45 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
     348             :                                      &refund->refund_fee),
     349          45 :         GNUNET_PQ_result_spec_uint64 ("refund_serial_id",
     350             :                                       &serial_id),
     351             :         GNUNET_PQ_result_spec_end
     352             :       };
     353             : 
     354          45 :       if (GNUNET_OK !=
     355          45 :           GNUNET_PQ_extract_result (result,
     356             :                                     rs,
     357             :                                     i))
     358             :       {
     359           0 :         GNUNET_break (0);
     360           0 :         GNUNET_free (refund);
     361           0 :         chc->failed = true;
     362           0 :         return;
     363             :       }
     364             :     }
     365          45 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     366          45 :     tl->next = chc->head;
     367          45 :     tl->type = TALER_EXCHANGEDB_TT_REFUND;
     368          45 :     tl->details.refund = refund;
     369          45 :     tl->serial_id = serial_id;
     370          45 :     tl->coin_history_id = chc->chid;
     371          45 :     chc->head = tl;
     372             :   }
     373             : }
     374             : 
     375             : 
     376             : /**
     377             :  * Function to be called with the results of a SELECT statement
     378             :  * that has returned @a num_results results.
     379             :  *
     380             :  * @param cls closure of type `struct CoinHistoryContext`
     381             :  * @param result the postgres result
     382             :  * @param num_results the number of results in @a result
     383             :  */
     384             : static void
     385           0 : add_coin_purse_decision (void *cls,
     386             :                          PGresult *result,
     387             :                          unsigned int num_results)
     388             : {
     389           0 :   struct CoinHistoryContext *chc = cls;
     390           0 :   struct PostgresClosure *pg = chc->pg;
     391             : 
     392           0 :   for (unsigned int i = 0; i<num_results; i++)
     393             :   {
     394             :     struct TALER_EXCHANGEDB_PurseRefundListEntry *prefund;
     395             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     396             :     uint64_t serial_id;
     397             : 
     398           0 :     prefund = GNUNET_new (struct TALER_EXCHANGEDB_PurseRefundListEntry);
     399             :     {
     400           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
     401           0 :         GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
     402             :                                               &prefund->purse_pub),
     403           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
     404             :                                      &prefund->refund_amount),
     405           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
     406             :                                      &prefund->refund_fee),
     407           0 :         GNUNET_PQ_result_spec_uint64 ("purse_decision_serial_id",
     408             :                                       &serial_id),
     409             :         GNUNET_PQ_result_spec_end
     410             :       };
     411             : 
     412           0 :       if (GNUNET_OK !=
     413           0 :           GNUNET_PQ_extract_result (result,
     414             :                                     rs,
     415             :                                     i))
     416             :       {
     417           0 :         GNUNET_break (0);
     418           0 :         GNUNET_free (prefund);
     419           0 :         chc->failed = true;
     420           0 :         return;
     421             :       }
     422             :     }
     423           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     424           0 :     tl->next = chc->head;
     425           0 :     tl->type = TALER_EXCHANGEDB_TT_PURSE_REFUND;
     426           0 :     tl->details.purse_refund = prefund;
     427           0 :     tl->serial_id = serial_id;
     428           0 :     tl->coin_history_id = chc->chid;
     429           0 :     chc->head = tl;
     430             :   }
     431             : }
     432             : 
     433             : 
     434             : /**
     435             :  * Function to be called with the results of a SELECT statement
     436             :  * that has returned @a num_results results.
     437             :  *
     438             :  * @param cls closure of type `struct CoinHistoryContext`
     439             :  * @param result the postgres result
     440             :  * @param num_results the number of results in @a result
     441             :  */
     442             : static void
     443           0 : add_old_coin_recoup (void *cls,
     444             :                      PGresult *result,
     445             :                      unsigned int num_results)
     446             : {
     447           0 :   struct CoinHistoryContext *chc = cls;
     448           0 :   struct PostgresClosure *pg = chc->pg;
     449             : 
     450           0 :   for (unsigned int i = 0; i<num_results; i++)
     451             :   {
     452             :     struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
     453             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     454             :     uint64_t serial_id;
     455             : 
     456           0 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
     457             :     {
     458           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
     459           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
     460             :                                               &recoup->coin.coin_pub),
     461           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
     462             :                                               &recoup->coin_sig),
     463           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
     464             :                                               &recoup->coin_blind),
     465           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
     466             :                                      &recoup->value),
     467           0 :         GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
     468             :                                          &recoup->timestamp),
     469           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
     470             :                                               &recoup->coin.denom_pub_hash),
     471           0 :         TALER_PQ_result_spec_denom_sig ("denom_sig",
     472             :                                         &recoup->coin.denom_sig),
     473           0 :         GNUNET_PQ_result_spec_uint64 ("recoup_refresh_uuid",
     474             :                                       &serial_id),
     475             :         GNUNET_PQ_result_spec_end
     476             :       };
     477             : 
     478           0 :       if (GNUNET_OK !=
     479           0 :           GNUNET_PQ_extract_result (result,
     480             :                                     rs,
     481             :                                     i))
     482             :       {
     483           0 :         GNUNET_break (0);
     484           0 :         GNUNET_free (recoup);
     485           0 :         chc->failed = true;
     486           0 :         return;
     487             :       }
     488           0 :       recoup->old_coin_pub = *chc->coin_pub;
     489             :     }
     490           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     491           0 :     tl->next = chc->head;
     492           0 :     tl->type = TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP;
     493           0 :     tl->details.old_coin_recoup = recoup;
     494           0 :     tl->serial_id = serial_id;
     495           0 :     tl->coin_history_id = chc->chid;
     496           0 :     chc->head = tl;
     497             :   }
     498             : }
     499             : 
     500             : 
     501             : /**
     502             :  * Function to be called with the results of a SELECT statement
     503             :  * that has returned @a num_results results.
     504             :  *
     505             :  * @param cls closure of type `struct CoinHistoryContext`
     506             :  * @param result the postgres result
     507             :  * @param num_results the number of results in @a result
     508             :  */
     509             : static void
     510           0 : add_coin_recoup (void *cls,
     511             :                  PGresult *result,
     512             :                  unsigned int num_results)
     513             : {
     514           0 :   struct CoinHistoryContext *chc = cls;
     515           0 :   struct PostgresClosure *pg = chc->pg;
     516             : 
     517           0 :   for (unsigned int i = 0; i<num_results; i++)
     518             :   {
     519             :     struct TALER_EXCHANGEDB_RecoupListEntry *recoup;
     520             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     521             :     uint64_t serial_id;
     522             : 
     523           0 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupListEntry);
     524             :     {
     525           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
     526           0 :         GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
     527             :                                               &recoup->reserve_pub),
     528           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
     529             :                                               &recoup->coin_sig),
     530           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
     531             :                                               &recoup->h_denom_pub),
     532           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
     533             :                                               &recoup->coin_blind),
     534           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
     535             :                                      &recoup->value),
     536           0 :         GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
     537             :                                          &recoup->timestamp),
     538           0 :         GNUNET_PQ_result_spec_uint64 ("recoup_uuid",
     539             :                                       &serial_id),
     540             :         GNUNET_PQ_result_spec_end
     541             :       };
     542             : 
     543           0 :       if (GNUNET_OK !=
     544           0 :           GNUNET_PQ_extract_result (result,
     545             :                                     rs,
     546             :                                     i))
     547             :       {
     548           0 :         GNUNET_break (0);
     549           0 :         GNUNET_free (recoup);
     550           0 :         chc->failed = true;
     551           0 :         return;
     552             :       }
     553             :     }
     554           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     555           0 :     tl->next = chc->head;
     556           0 :     tl->type = TALER_EXCHANGEDB_TT_RECOUP;
     557           0 :     tl->details.recoup = recoup;
     558           0 :     tl->serial_id = serial_id;
     559           0 :     tl->coin_history_id = chc->chid;
     560           0 :     chc->head = tl;
     561             :   }
     562             : }
     563             : 
     564             : 
     565             : /**
     566             :  * Function to be called with the results of a SELECT statement
     567             :  * that has returned @a num_results results.
     568             :  *
     569             :  * @param cls closure of type `struct CoinHistoryContext`
     570             :  * @param result the postgres result
     571             :  * @param num_results the number of results in @a result
     572             :  */
     573             : static void
     574           0 : add_coin_recoup_refresh (void *cls,
     575             :                          PGresult *result,
     576             :                          unsigned int num_results)
     577             : {
     578           0 :   struct CoinHistoryContext *chc = cls;
     579           0 :   struct PostgresClosure *pg = chc->pg;
     580             : 
     581           0 :   for (unsigned int i = 0; i<num_results; i++)
     582             :   {
     583             :     struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
     584             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     585             :     uint64_t serial_id;
     586             : 
     587           0 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
     588             :     {
     589           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
     590           0 :         GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
     591             :                                               &recoup->old_coin_pub),
     592           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
     593             :                                               &recoup->coin_sig),
     594           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
     595             :                                               &recoup->coin_blind),
     596           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
     597             :                                      &recoup->value),
     598           0 :         GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
     599             :                                          &recoup->timestamp),
     600           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
     601             :                                               &recoup->coin.denom_pub_hash),
     602           0 :         TALER_PQ_result_spec_denom_sig ("denom_sig",
     603             :                                         &recoup->coin.denom_sig),
     604           0 :         GNUNET_PQ_result_spec_uint64 ("recoup_refresh_uuid",
     605             :                                       &serial_id),
     606             :         GNUNET_PQ_result_spec_end
     607             :       };
     608             : 
     609           0 :       if (GNUNET_OK !=
     610           0 :           GNUNET_PQ_extract_result (result,
     611             :                                     rs,
     612             :                                     i))
     613             :       {
     614           0 :         GNUNET_break (0);
     615           0 :         GNUNET_free (recoup);
     616           0 :         chc->failed = true;
     617           0 :         return;
     618             :       }
     619           0 :       recoup->coin.coin_pub = *chc->coin_pub;
     620             :     }
     621           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     622           0 :     tl->next = chc->head;
     623           0 :     tl->type = TALER_EXCHANGEDB_TT_RECOUP_REFRESH;
     624           0 :     tl->details.recoup_refresh = recoup;
     625           0 :     tl->serial_id = serial_id;
     626           0 :     tl->coin_history_id = chc->chid;
     627           0 :     chc->head = tl;
     628             :   }
     629             : }
     630             : 
     631             : 
     632             : /**
     633             :  * Function to be called with the results of a SELECT statement
     634             :  * that has returned @a num_results results.
     635             :  *
     636             :  * @param cls closure of type `struct CoinHistoryContext`
     637             :  * @param result the postgres result
     638             :  * @param num_results the number of results in @a result
     639             :  */
     640             : static void
     641           0 : add_coin_reserve_open (void *cls,
     642             :                        PGresult *result,
     643             :                        unsigned int num_results)
     644             : {
     645           0 :   struct CoinHistoryContext *chc = cls;
     646           0 :   struct PostgresClosure *pg = chc->pg;
     647             : 
     648           0 :   for (unsigned int i = 0; i<num_results; i++)
     649             :   {
     650             :     struct TALER_EXCHANGEDB_ReserveOpenListEntry *role;
     651             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     652             :     uint64_t serial_id;
     653             : 
     654           0 :     role = GNUNET_new (struct TALER_EXCHANGEDB_ReserveOpenListEntry);
     655             :     {
     656           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
     657           0 :         GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
     658             :                                               &role->reserve_sig),
     659           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
     660             :                                               &role->coin_sig),
     661           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("contribution",
     662             :                                      &role->coin_contribution),
     663           0 :         GNUNET_PQ_result_spec_uint64 ("reserve_open_deposit_uuid",
     664             :                                       &serial_id),
     665             :         GNUNET_PQ_result_spec_end
     666             :       };
     667             : 
     668           0 :       if (GNUNET_OK !=
     669           0 :           GNUNET_PQ_extract_result (result,
     670             :                                     rs,
     671             :                                     i))
     672             :       {
     673           0 :         GNUNET_break (0);
     674           0 :         GNUNET_free (role);
     675           0 :         chc->failed = true;
     676           0 :         return;
     677             :       }
     678             :     }
     679           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     680           0 :     tl->next = chc->head;
     681           0 :     tl->type = TALER_EXCHANGEDB_TT_RESERVE_OPEN;
     682           0 :     tl->details.reserve_open = role;
     683           0 :     tl->serial_id = serial_id;
     684           0 :     tl->coin_history_id = chc->chid;
     685           0 :     chc->head = tl;
     686             :   }
     687             : }
     688             : 
     689             : 
     690             : /**
     691             :  * Work we need to do.
     692             :  */
     693             : struct Work
     694             : {
     695             :   /**
     696             :    * Name of the table.
     697             :    */
     698             :   const char *table;
     699             : 
     700             :   /**
     701             :    * SQL prepared statement name.
     702             :    */
     703             :   const char *statement;
     704             : 
     705             :   /**
     706             :    * Function to call to handle the result(s).
     707             :    */
     708             :   GNUNET_PQ_PostgresResultHandler cb;
     709             : };
     710             : 
     711             : 
     712             : /**
     713             :  * We found a coin history entry. Lookup details
     714             :  * from the respective table and store in @a cls.
     715             :  *
     716             :  * @param[in,out] cls a `struct CoinHistoryContext`
     717             :  * @param result a coin history entry result set
     718             :  * @param num_results total number of results in @a results
     719             :  */
     720             : static void
     721         148 : handle_history_entry (void *cls,
     722             :                       PGresult *result,
     723             :                       unsigned int num_results)
     724             : {
     725         148 :   struct CoinHistoryContext *chc = cls;
     726         148 :   struct PostgresClosure *pg = chc->pg;
     727             :   static const struct Work work[] = {
     728             :     [TALER_EXCHANGEDB_TT_DEPOSIT] =
     729             :     { "coin_deposits",
     730             :       "get_deposit_with_coin_pub",
     731             :       &add_coin_deposit },
     732             :     [TALER_EXCHANGEDB_TT_MELT] =
     733             :     { "refresh",
     734             :       "get_refresh_by_coin",
     735             :       &add_coin_melt },
     736             :     [TALER_EXCHANGEDB_TT_PURSE_DEPOSIT] =
     737             :     { "purse_deposits",
     738             :       "get_purse_deposit_by_coin_pub",
     739             :       &add_coin_purse_deposit },
     740             :     [TALER_EXCHANGEDB_TT_PURSE_REFUND] =
     741             :     { "purse_decision",
     742             :       "get_purse_decision_by_coin_pub",
     743             :       &add_coin_purse_decision },
     744             :     [TALER_EXCHANGEDB_TT_REFUND] =
     745             :     { "refunds",
     746             :       "get_refunds_by_coin",
     747             :       &add_coin_refund },
     748             :     [TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP] =
     749             :     { "recoup_refresh::OLD",
     750             :       "recoup_by_old_coin",
     751             :       &add_old_coin_recoup },
     752             :     [TALER_EXCHANGEDB_TT_RECOUP] =
     753             :     { "recoup",
     754             :       "recoup_by_coin",
     755             :       &add_coin_recoup },
     756             :     [TALER_EXCHANGEDB_TT_RECOUP_REFRESH] =
     757             :     { "recoup_refresh::NEW",
     758             :       "recoup_by_refreshed_coin",
     759             :       &add_coin_recoup_refresh },
     760             :     [TALER_EXCHANGEDB_TT_RESERVE_OPEN] =
     761             :     { "reserves_open_deposits",
     762             :       "reserve_open_by_coin",
     763             :       &add_coin_reserve_open },
     764             :     { NULL, NULL, NULL }
     765             :   };
     766             :   char *table_name;
     767             :   uint64_t serial_id;
     768         148 :   struct GNUNET_PQ_ResultSpec rs[] = {
     769         148 :     GNUNET_PQ_result_spec_string ("table_name",
     770             :                                   &table_name),
     771         148 :     GNUNET_PQ_result_spec_uint64 ("serial_id",
     772             :                                   &serial_id),
     773         148 :     GNUNET_PQ_result_spec_uint64 ("coin_history_serial_id",
     774             :                                   &chc->chid),
     775             :     GNUNET_PQ_result_spec_end
     776             :   };
     777         148 :   struct GNUNET_PQ_QueryParam params[] = {
     778         148 :     GNUNET_PQ_query_param_auto_from_type (chc->coin_pub),
     779         148 :     GNUNET_PQ_query_param_uint64 (&serial_id),
     780             :     GNUNET_PQ_query_param_end
     781             :   };
     782             : 
     783         537 :   for (unsigned int i = 0; i<num_results; i++)
     784             :   {
     785             :     enum GNUNET_DB_QueryStatus qs;
     786         389 :     bool found = false;
     787             : 
     788         389 :     if (GNUNET_OK !=
     789         389 :         GNUNET_PQ_extract_result (result,
     790             :                                   rs,
     791             :                                   i))
     792             :     {
     793           0 :       GNUNET_break (0);
     794           0 :       chc->failed = true;
     795           0 :       return;
     796             :     }
     797             : 
     798         389 :     for (unsigned int s = 0;
     799         695 :          NULL != work[s].statement;
     800         306 :          s++)
     801             :     {
     802        1001 :       if (0 != strcmp (table_name,
     803         695 :                        work[s].table))
     804         306 :         continue;
     805         389 :       found = true;
     806         389 :       qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
     807         389 :                                                  work[s].statement,
     808             :                                                  params,
     809         389 :                                                  work[s].cb,
     810             :                                                  chc);
     811         389 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     812             :                   "Coin %s had %d transactions at %llu in table %s\n",
     813             :                   TALER_B2S (chc->coin_pub),
     814             :                   (int) qs,
     815             :                   (unsigned long long) serial_id,
     816             :                   table_name);
     817         389 :       if (0 > qs)
     818           0 :         chc->failed = true;
     819         389 :       break;
     820             :     }
     821         389 :     if (! found)
     822             :     {
     823           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     824             :                   "Coin history includes unsupported table `%s`\n",
     825             :                   table_name);
     826           0 :       chc->failed = true;
     827             :     }
     828         389 :     GNUNET_PQ_cleanup_result (rs);
     829         389 :     if (chc->failed)
     830           0 :       break;
     831             :   }
     832             : }
     833             : 
     834             : 
     835             : enum GNUNET_DB_QueryStatus
     836         148 : TEH_PG_get_coin_transactions (
     837             :   void *cls,
     838             :   bool begin_transaction,
     839             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     840             :   uint64_t start_off,
     841             :   uint64_t etag_in,
     842             :   uint64_t *etag_out,
     843             :   struct TALER_Amount *balance,
     844             :   struct TALER_DenominationHashP *h_denom_pub,
     845             :   struct TALER_EXCHANGEDB_TransactionList **tlp)
     846             : {
     847         148 :   struct PostgresClosure *pg = cls;
     848         148 :   struct GNUNET_PQ_QueryParam params[] = {
     849         148 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
     850             :     GNUNET_PQ_query_param_end
     851             :   };
     852         148 :   struct GNUNET_PQ_QueryParam lparams[] = {
     853         148 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
     854         148 :     GNUNET_PQ_query_param_uint64 (&start_off),
     855             :     GNUNET_PQ_query_param_end
     856             :   };
     857         148 :   struct CoinHistoryContext chc = {
     858             :     .head = NULL,
     859             :     .coin_pub = coin_pub,
     860             :     .pg = pg
     861             :   };
     862             : 
     863         148 :   *tlp = NULL;
     864         148 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     865             :               "Getting transactions for coin %s\n",
     866             :               TALER_B2S (coin_pub));
     867         148 :   PREPARE (pg,
     868             :            "get_coin_history_etag_balance",
     869             :            "SELECT"
     870             :            " ch.coin_history_serial_id"
     871             :            ",kc.remaining"
     872             :            ",denom.denom_pub_hash"
     873             :            " FROM coin_history ch"
     874             :            " JOIN known_coins kc"
     875             :            "   USING (coin_pub)"
     876             :            " JOIN denominations denom"
     877             :            "   USING (denominations_serial)"
     878             :            " WHERE coin_pub=$1"
     879             :            " ORDER BY coin_history_serial_id DESC"
     880             :            " LIMIT 1;");
     881         148 :   PREPARE (pg,
     882             :            "get_coin_history",
     883             :            "SELECT"
     884             :            " table_name"
     885             :            ",serial_id"
     886             :            ",coin_history_serial_id"
     887             :            " FROM coin_history"
     888             :            " WHERE coin_pub=$1"
     889             :            "   AND coin_history_serial_id > $2"
     890             :            " ORDER BY coin_history_serial_id DESC;");
     891         148 :   PREPARE (pg,
     892             :            "get_deposit_with_coin_pub",
     893             :            "SELECT"
     894             :            " cdep.amount_with_fee"
     895             :            ",denoms.fee_deposit"
     896             :            ",denoms.denom_pub_hash"
     897             :            ",kc.age_commitment_hash"
     898             :            ",bdep.wallet_timestamp"
     899             :            ",bdep.refund_deadline"
     900             :            ",bdep.wire_deadline"
     901             :            ",bdep.merchant_pub"
     902             :            ",bdep.h_contract_terms"
     903             :            ",bdep.wallet_data_hash"
     904             :            ",bdep.wire_salt"
     905             :            ",wt.payto_uri"
     906             :            ",cdep.coin_sig"
     907             :            ",cdep.coin_deposit_serial_id"
     908             :            ",bdep.done"
     909             :            " FROM coin_deposits cdep"
     910             :            " JOIN batch_deposits bdep"
     911             :            "   USING (batch_deposit_serial_id)"
     912             :            " JOIN wire_targets wt"
     913             :            "   USING (wire_target_h_payto)"
     914             :            " JOIN known_coins kc"
     915             :            "   ON (kc.coin_pub = cdep.coin_pub)"
     916             :            " JOIN denominations denoms"
     917             :            "   USING (denominations_serial)"
     918             :            " WHERE cdep.coin_pub=$1"
     919             :            "   AND cdep.coin_deposit_serial_id=$2;");
     920         148 :   PREPARE (pg,
     921             :            "get_refresh_by_coin",
     922             :            "SELECT"
     923             :            " rc"
     924             :            ",refresh_seed"
     925             :            ",blinding_seed"
     926             :            ",old_coin_sig"
     927             :            ",amount_with_fee"
     928             :            ",denoms.denom_pub_hash"
     929             :            ",denoms.fee_refresh"
     930             :            ",kc.age_commitment_hash"
     931             :            ",refresh_id"
     932             :            " FROM refresh"
     933             :            " JOIN known_coins kc"
     934             :            "   ON (refresh.old_coin_pub = kc.coin_pub)"
     935             :            " JOIN denominations denoms"
     936             :            "   USING (denominations_serial)"
     937             :            " WHERE old_coin_pub=$1"
     938             :            "   AND refresh_id=$2;");
     939         148 :   PREPARE (pg,
     940             :            "get_purse_deposit_by_coin_pub",
     941             :            "SELECT"
     942             :            " partner_base_url"
     943             :            ",pd.amount_with_fee"
     944             :            ",denoms.fee_deposit"
     945             :            ",denoms.denom_pub_hash"
     946             :            ",pd.purse_pub"
     947             :            ",kc.age_commitment_hash"
     948             :            ",pd.coin_sig"
     949             :            ",pd.purse_deposit_serial_id"
     950             :            ",pdes.refunded"
     951             :            " FROM purse_deposits pd"
     952             :            " LEFT JOIN partners"
     953             :            "   USING (partner_serial_id)"
     954             :            " JOIN purse_requests pr"
     955             :            "   USING (purse_pub)"
     956             :            " LEFT JOIN purse_decision pdes"
     957             :            "   USING (purse_pub)"
     958             :            " JOIN known_coins kc"
     959             :            "   ON (pd.coin_pub = kc.coin_pub)"
     960             :            " JOIN denominations denoms"
     961             :            "   USING (denominations_serial)"
     962             :            " WHERE pd.purse_deposit_serial_id=$2"
     963             :            "   AND pd.coin_pub=$1;");
     964         148 :   PREPARE (pg,
     965             :            "get_purse_decision_by_coin_pub",
     966             :            "SELECT"
     967             :            " pdes.purse_pub"
     968             :            ",pd.amount_with_fee"
     969             :            ",denom.fee_refund"
     970             :            ",pdes.purse_decision_serial_id"
     971             :            " FROM purse_decision pdes"
     972             :            " JOIN purse_deposits pd"
     973             :            "   USING (purse_pub)"
     974             :            " JOIN known_coins kc"
     975             :            "   ON (pd.coin_pub = kc.coin_pub)"
     976             :            " JOIN denominations denom"
     977             :            "   USING (denominations_serial)"
     978             :            " WHERE pd.coin_pub=$1"
     979             :            "   AND pdes.purse_decision_serial_id=$2"
     980             :            "   AND pdes.refunded;");
     981         148 :   PREPARE (pg,
     982             :            "get_refunds_by_coin",
     983             :            "SELECT"
     984             :            " bdep.merchant_pub"
     985             :            ",ref.merchant_sig"
     986             :            ",bdep.h_contract_terms"
     987             :            ",ref.rtransaction_id"
     988             :            ",ref.amount_with_fee"
     989             :            ",denom.fee_refund"
     990             :            ",ref.refund_serial_id"
     991             :            " FROM refunds ref"
     992             :            " JOIN coin_deposits cdep"
     993             :            "   ON (ref.coin_pub = cdep.coin_pub AND ref.batch_deposit_serial_id = cdep.batch_deposit_serial_id)"
     994             :            " JOIN batch_deposits bdep"
     995             :            "   ON (ref.batch_deposit_serial_id = bdep.batch_deposit_serial_id)"
     996             :            " JOIN known_coins kc"
     997             :            "   ON (ref.coin_pub = kc.coin_pub)"
     998             :            " JOIN denominations denom"
     999             :            "   USING (denominations_serial)"
    1000             :            " WHERE ref.coin_pub=$1"
    1001             :            "   AND ref.refund_serial_id=$2;");
    1002         148 :   PREPARE (pg,
    1003             :            "recoup_by_old_coin",
    1004             :            "SELECT"
    1005             :            " coins.coin_pub"
    1006             :            ",rr.coin_sig"
    1007             :            ",rr.coin_blind"
    1008             :            ",rr.amount"
    1009             :            ",rr.recoup_timestamp"
    1010             :            ",denoms.denom_pub_hash"
    1011             :            ",coins.denom_sig"
    1012             :            ",rr.recoup_refresh_uuid"
    1013             :            " FROM recoup_refresh rr"
    1014             :            " JOIN known_coins coins"
    1015             :            "   USING (coin_pub)"
    1016             :            " JOIN denominations denoms"
    1017             :            "   USING (denominations_serial)"
    1018             :            " WHERE recoup_refresh_uuid=$2"
    1019             :            "   AND refresh_id IN"
    1020             :            "   (SELECT refresh_id"
    1021             :            "    FROM refresh"
    1022             :            "    WHERE refresh.old_coin_pub=$1);");
    1023         148 :   PREPARE (pg,
    1024             :            "recoup_by_coin",
    1025             :            "SELECT"
    1026             :            " res.reserve_pub"
    1027             :            ",denoms.denom_pub_hash"
    1028             :            ",rcp.coin_sig"
    1029             :            ",rcp.coin_blind"
    1030             :            ",rcp.amount"
    1031             :            ",rcp.recoup_timestamp"
    1032             :            ",rcp.recoup_uuid"
    1033             :            " FROM recoup rcp"
    1034             :            " JOIN withdraw ro"
    1035             :            "   USING (withdraw_id)"
    1036             :            " JOIN reserves res"
    1037             :            "   USING (reserve_pub)"
    1038             :            " JOIN known_coins coins"
    1039             :            "   USING (coin_pub)"
    1040             :            " JOIN denominations denoms"
    1041             :            "   ON (denoms.denominations_serial = coins.denominations_serial)"
    1042             :            " WHERE rcp.recoup_uuid=$2"
    1043             :            "   AND coins.coin_pub=$1;");
    1044             :   /* Used to obtain recoup transactions
    1045             :      for a refreshed coin */
    1046         148 :   PREPARE (pg,
    1047             :            "recoup_by_refreshed_coin",
    1048             :            "SELECT"
    1049             :            " old_coins.coin_pub AS old_coin_pub"
    1050             :            ",rr.coin_sig"
    1051             :            ",rr.coin_blind"
    1052             :            ",rr.amount"
    1053             :            ",rr.recoup_timestamp"
    1054             :            ",denoms.denom_pub_hash"
    1055             :            ",coins.denom_sig"
    1056             :            ",recoup_refresh_uuid"
    1057             :            " FROM recoup_refresh rr"
    1058             :            "    JOIN refresh rfc"
    1059             :            "      ON (rr.refresh_id = rfc.refresh_id)"
    1060             :            "    JOIN known_coins old_coins"
    1061             :            "      ON (rfc.old_coin_pub = old_coins.coin_pub)"
    1062             :            "    JOIN known_coins coins"
    1063             :            "      ON (rr.coin_pub = coins.coin_pub)"
    1064             :            "    JOIN denominations denoms"
    1065             :            "      ON (denoms.denominations_serial = coins.denominations_serial)"
    1066             :            " WHERE rr.recoup_refresh_uuid=$2"
    1067             :            "   AND coins.coin_pub=$1;");
    1068         148 :   PREPARE (pg,
    1069             :            "reserve_open_by_coin",
    1070             :            "SELECT"
    1071             :            " reserve_open_deposit_uuid"
    1072             :            ",coin_sig"
    1073             :            ",reserve_sig"
    1074             :            ",contribution"
    1075             :            " FROM reserves_open_deposits"
    1076             :            " WHERE coin_pub=$1"
    1077             :            "   AND reserve_open_deposit_uuid=$2;");
    1078         148 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1079             :               "  --- landed here 1\n");
    1080         148 :   for (unsigned int i = 0; i<RETRIES; i++)
    1081             :   {
    1082             :     enum GNUNET_DB_QueryStatus qs;
    1083             :     uint64_t end;
    1084         148 :     struct GNUNET_PQ_ResultSpec rs[] = {
    1085         148 :       GNUNET_PQ_result_spec_uint64 ("coin_history_serial_id",
    1086             :                                     &end),
    1087         148 :       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    1088             :                                             h_denom_pub),
    1089         148 :       TALER_PQ_RESULT_SPEC_AMOUNT ("remaining",
    1090             :                                    balance),
    1091             :       GNUNET_PQ_result_spec_end
    1092             :     };
    1093             : 
    1094         148 :     if (begin_transaction)
    1095             :     {
    1096           4 :       if (GNUNET_OK !=
    1097           4 :           TEH_PG_start_read_committed (pg,
    1098             :                                        "get-coin-transactions"))
    1099             :       {
    1100           0 :         GNUNET_break (0);
    1101         148 :         return GNUNET_DB_STATUS_HARD_ERROR;
    1102             :       }
    1103             :     }
    1104             :     /* First only check the last item, to see if
    1105             :        we even need to iterate */
    1106         148 :     qs = GNUNET_PQ_eval_prepared_singleton_select (
    1107             :       pg->conn,
    1108             :       "get_coin_history_etag_balance",
    1109             :       params,
    1110             :       rs);
    1111         148 :     switch (qs)
    1112             :     {
    1113           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    1114           0 :       if (begin_transaction)
    1115           0 :         TEH_PG_rollback (pg);
    1116           0 :       return qs;
    1117           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    1118           0 :       if (begin_transaction)
    1119           0 :         TEH_PG_rollback (pg);
    1120           0 :       continue;
    1121           0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1122           0 :       if (begin_transaction)
    1123           0 :         TEH_PG_rollback (pg);
    1124           0 :       return qs;
    1125         148 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1126         148 :       *etag_out = end;
    1127         148 :       if (end == etag_in)
    1128           0 :         return qs;
    1129             :     }
    1130             :     /* We indeed need to iterate over the history */
    1131         148 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1132             :                 "Current ETag for coin %s is %llu\n",
    1133             :                 TALER_B2S (coin_pub),
    1134             :                 (unsigned long long) end);
    1135             : 
    1136         148 :     qs = GNUNET_PQ_eval_prepared_multi_select (
    1137             :       pg->conn,
    1138             :       "get_coin_history",
    1139             :       lparams,
    1140             :       &handle_history_entry,
    1141             :       &chc);
    1142         148 :     switch (qs)
    1143             :     {
    1144           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    1145           0 :       if (begin_transaction)
    1146           0 :         TEH_PG_rollback (pg);
    1147           0 :       return qs;
    1148           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    1149           0 :       if (begin_transaction)
    1150           0 :         TEH_PG_rollback (pg);
    1151           0 :       continue;
    1152         148 :     default:
    1153         148 :       break;
    1154             :     }
    1155         148 :     if (chc.failed)
    1156             :     {
    1157           0 :       if (begin_transaction)
    1158           0 :         TEH_PG_rollback (pg);
    1159           0 :       TEH_COMMON_free_coin_transaction_list (pg,
    1160             :                                              chc.head);
    1161           0 :       return GNUNET_DB_STATUS_SOFT_ERROR;
    1162             :     }
    1163         148 :     if (! begin_transaction)
    1164             :     {
    1165         144 :       *tlp = chc.head;
    1166         144 :       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1167             :     }
    1168           4 :     qs = TEH_PG_commit (pg);
    1169           4 :     switch (qs)
    1170             :     {
    1171           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    1172           0 :       TEH_COMMON_free_coin_transaction_list (pg,
    1173             :                                              chc.head);
    1174           0 :       chc.head = NULL;
    1175           0 :       return qs;
    1176           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    1177           0 :       TEH_COMMON_free_coin_transaction_list (pg,
    1178             :                                              chc.head);
    1179           0 :       chc.head = NULL;
    1180           0 :       continue;
    1181           4 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1182             :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1183           4 :       *tlp = chc.head;
    1184           4 :       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1185             :     }
    1186             :   }
    1187           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1188             :               "  --- landed here 2\n");
    1189           0 :   return GNUNET_DB_STATUS_SOFT_ERROR;
    1190             : }

Generated by: LCOV version 1.16