LCOV - code coverage report
Current view: top level - exchangedb - get_coin_transactions.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 36.3 % 377 137
Test Date: 2026-05-12 15:34:29 Functions: 36.4 % 11 4

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

Generated by: LCOV version 2.0-1