LCOV - code coverage report
Current view: top level - exchangedb - get_coin_transactions.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 36.5 % 375 137
Test Date: 2026-04-14 15:39:31 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 "taler/taler_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_uint64 ("reserve_open_deposit_uuid",
     662              :                                       &serial_id),
     663              :         GNUNET_PQ_result_spec_end
     664              :       };
     665              : 
     666            0 :       if (GNUNET_OK !=
     667            0 :           GNUNET_PQ_extract_result (result,
     668              :                                     rs,
     669              :                                     i))
     670              :       {
     671            0 :         GNUNET_break (0);
     672            0 :         GNUNET_free (role);
     673            0 :         chc->failed = true;
     674            0 :         return;
     675              :       }
     676              :     }
     677            0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     678            0 :     tl->next = chc->head;
     679            0 :     tl->type = TALER_EXCHANGEDB_TT_RESERVE_OPEN;
     680            0 :     tl->details.reserve_open = role;
     681            0 :     tl->serial_id = serial_id;
     682            0 :     tl->coin_history_id = chc->chid;
     683            0 :     chc->head = tl;
     684              :   }
     685              : }
     686              : 
     687              : 
     688              : /**
     689              :  * Work we need to do.
     690              :  */
     691              : struct Work
     692              : {
     693              :   /**
     694              :    * Name of the table.
     695              :    */
     696              :   const char *table;
     697              : 
     698              :   /**
     699              :    * SQL prepared statement name.
     700              :    */
     701              :   const char *statement;
     702              : 
     703              :   /**
     704              :    * Function to call to handle the result(s).
     705              :    */
     706              :   GNUNET_PQ_PostgresResultHandler cb;
     707              : };
     708              : 
     709              : 
     710              : /**
     711              :  * We found a coin history entry. Lookup details
     712              :  * from the respective table and store in @a cls.
     713              :  *
     714              :  * @param[in,out] cls a `struct CoinHistoryContext`
     715              :  * @param result a coin history entry result set
     716              :  * @param num_results total number of results in @a results
     717              :  */
     718              : static void
     719            4 : handle_history_entry (void *cls,
     720              :                       PGresult *result,
     721              :                       unsigned int num_results)
     722              : {
     723            4 :   struct CoinHistoryContext *chc = cls;
     724            4 :   struct TALER_EXCHANGEDB_PostgresContext *pg = chc->pg;
     725              :   static const struct Work work[] = {
     726              :     [TALER_EXCHANGEDB_TT_DEPOSIT] =
     727              :     { "coin_deposits",
     728              :       "get_deposit_with_coin_pub",
     729              :       &add_coin_deposit },
     730              :     [TALER_EXCHANGEDB_TT_MELT] =
     731              :     { "refresh",
     732              :       "get_refresh_by_coin",
     733              :       &add_coin_melt },
     734              :     [TALER_EXCHANGEDB_TT_PURSE_DEPOSIT] =
     735              :     { "purse_deposits",
     736              :       "get_purse_deposit_by_coin_pub",
     737              :       &add_coin_purse_deposit },
     738              :     [TALER_EXCHANGEDB_TT_PURSE_REFUND] =
     739              :     { "purse_decision",
     740              :       "get_purse_decision_by_coin_pub",
     741              :       &add_coin_purse_decision },
     742              :     [TALER_EXCHANGEDB_TT_REFUND] =
     743              :     { "refunds",
     744              :       "get_refunds_by_coin",
     745              :       &add_coin_refund },
     746              :     [TALER_EXCHANGEDB_TT_RECOUP_WITHDRAW] =
     747              :     { "recoup",
     748              :       "recoup_by_coin",
     749              :       &add_coin_recoup },
     750              :     [TALER_EXCHANGEDB_TT_RECOUP_REFRESH] =
     751              :     { "recoup_refresh::NEW",
     752              :       "recoup_by_refreshed_coin",
     753              :       &add_coin_recoup_refresh },
     754              :     [TALER_EXCHANGEDB_TT_RECOUP_REFRESH_RECEIVER] =
     755              :     { "recoup_refresh::OLD",
     756              :       "recoup_by_old_coin",
     757              :       &add_old_coin_recoup },
     758              :     [TALER_EXCHANGEDB_TT_RESERVE_OPEN] =
     759              :     { "reserves_open_deposits",
     760              :       "reserve_open_by_coin",
     761              :       &add_coin_reserve_open },
     762              :     { NULL, NULL, NULL }
     763              :   };
     764              :   char *table_name;
     765              :   uint64_t serial_id;
     766            4 :   struct GNUNET_PQ_ResultSpec rs[] = {
     767            4 :     GNUNET_PQ_result_spec_string ("table_name",
     768              :                                   &table_name),
     769            4 :     GNUNET_PQ_result_spec_uint64 ("serial_id",
     770              :                                   &serial_id),
     771            4 :     GNUNET_PQ_result_spec_uint64 ("coin_history_serial_id",
     772              :                                   &chc->chid),
     773              :     GNUNET_PQ_result_spec_end
     774              :   };
     775            4 :   struct GNUNET_PQ_QueryParam params[] = {
     776            4 :     GNUNET_PQ_query_param_auto_from_type (chc->coin_pub),
     777            4 :     GNUNET_PQ_query_param_uint64 (&serial_id),
     778              :     GNUNET_PQ_query_param_end
     779              :   };
     780              : 
     781            9 :   for (unsigned int i = 0; i<num_results; i++)
     782              :   {
     783              :     enum GNUNET_DB_QueryStatus qs;
     784            5 :     bool found = false;
     785              : 
     786            5 :     if (GNUNET_OK !=
     787            5 :         GNUNET_PQ_extract_result (result,
     788              :                                   rs,
     789              :                                   i))
     790              :     {
     791            0 :       GNUNET_break (0);
     792            0 :       chc->failed = true;
     793            0 :       return;
     794              :     }
     795              : 
     796            5 :     for (unsigned int s = 0;
     797           23 :          NULL != work[s].statement;
     798           18 :          s++)
     799              :     {
     800           41 :       if (0 != strcmp (table_name,
     801           23 :                        work[s].table))
     802           18 :         continue;
     803            5 :       found = true;
     804            5 :       qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
     805            5 :                                                  work[s].statement,
     806              :                                                  params,
     807            5 :                                                  work[s].cb,
     808              :                                                  chc);
     809            5 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     810              :                   "Coin %s had %d transactions at %llu in table %s\n",
     811              :                   TALER_B2S (chc->coin_pub),
     812              :                   (int) qs,
     813              :                   (unsigned long long) serial_id,
     814              :                   table_name);
     815            5 :       if (0 > qs)
     816            0 :         chc->failed = true;
     817            5 :       break;
     818              :     }
     819            5 :     if (! found)
     820              :     {
     821            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     822              :                   "Coin history includes unsupported table `%s`\n",
     823              :                   table_name);
     824            0 :       chc->failed = true;
     825              :     }
     826            5 :     GNUNET_PQ_cleanup_result (rs);
     827            5 :     if (chc->failed)
     828            0 :       break;
     829              :   }
     830              : }
     831              : 
     832              : 
     833              : enum GNUNET_DB_QueryStatus
     834            4 : TALER_EXCHANGEDB_get_coin_transactions (
     835              :   struct TALER_EXCHANGEDB_PostgresContext *pg,
     836              :   bool begin_transaction,
     837              :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     838              :   uint64_t start_off,
     839              :   uint64_t etag_in,
     840              :   uint64_t *etag_out,
     841              :   struct TALER_Amount *balance,
     842              :   struct TALER_DenominationHashP *h_denom_pub,
     843              :   struct TALER_EXCHANGEDB_TransactionList **tlp)
     844              : {
     845            4 :   struct GNUNET_PQ_QueryParam params[] = {
     846            4 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
     847              :     GNUNET_PQ_query_param_end
     848              :   };
     849            4 :   struct GNUNET_PQ_QueryParam lparams[] = {
     850            4 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
     851            4 :     GNUNET_PQ_query_param_uint64 (&start_off),
     852              :     GNUNET_PQ_query_param_end
     853              :   };
     854            4 :   struct CoinHistoryContext chc = {
     855              :     .head = NULL,
     856              :     .coin_pub = coin_pub,
     857              :     .pg = pg
     858              :   };
     859              : 
     860            4 :   *tlp = NULL;
     861            4 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     862              :               "Getting transactions for coin %s\n",
     863              :               TALER_B2S (coin_pub));
     864            4 :   PREPARE (pg,
     865              :            "get_coin_history_etag_balance",
     866              :            "SELECT"
     867              :            " ch.coin_history_serial_id"
     868              :            ",kc.remaining"
     869              :            ",denom.denom_pub_hash"
     870              :            " FROM coin_history ch"
     871              :            " JOIN known_coins kc"
     872              :            "   USING (coin_pub)"
     873              :            " JOIN denominations denom"
     874              :            "   USING (denominations_serial)"
     875              :            " WHERE coin_pub=$1"
     876              :            " ORDER BY coin_history_serial_id DESC"
     877              :            " LIMIT 1;");
     878            4 :   PREPARE (pg,
     879              :            "get_coin_history",
     880              :            "SELECT"
     881              :            " table_name"
     882              :            ",serial_id"
     883              :            ",coin_history_serial_id"
     884              :            " FROM coin_history"
     885              :            " WHERE coin_pub=$1"
     886              :            "   AND coin_history_serial_id > $2"
     887              :            " ORDER BY coin_history_serial_id DESC;");
     888            4 :   PREPARE (pg,
     889              :            "get_deposit_with_coin_pub",
     890              :            "SELECT"
     891              :            " cdep.amount_with_fee"
     892              :            ",denoms.fee_deposit"
     893              :            ",denoms.denom_pub_hash"
     894              :            ",kc.age_commitment_hash"
     895              :            ",bdep.wallet_timestamp"
     896              :            ",bdep.refund_deadline"
     897              :            ",bdep.wire_deadline"
     898              :            ",bdep.merchant_pub"
     899              :            ",bdep.h_contract_terms"
     900              :            ",bdep.wallet_data_hash"
     901              :            ",bdep.wire_salt"
     902              :            ",wt.payto_uri"
     903              :            ",cdep.coin_sig"
     904              :            ",cdep.coin_deposit_serial_id"
     905              :            ",bdep.done"
     906              :            " FROM coin_deposits cdep"
     907              :            " JOIN batch_deposits bdep"
     908              :            "   USING (batch_deposit_serial_id)"
     909              :            " JOIN wire_targets wt"
     910              :            "   USING (wire_target_h_payto)"
     911              :            " JOIN known_coins kc"
     912              :            "   ON (kc.coin_pub = cdep.coin_pub)"
     913              :            " JOIN denominations denoms"
     914              :            "   USING (denominations_serial)"
     915              :            " WHERE cdep.coin_pub=$1"
     916              :            "   AND cdep.coin_deposit_serial_id=$2;");
     917            4 :   PREPARE (pg,
     918              :            "get_refresh_by_coin",
     919              :            "SELECT"
     920              :            " rc"
     921              :            ",refresh_seed"
     922              :            ",blinding_seed"
     923              :            ",old_coin_sig"
     924              :            ",amount_with_fee"
     925              :            ",denoms.denom_pub_hash"
     926              :            ",denoms.fee_refresh"
     927              :            ",kc.age_commitment_hash"
     928              :            ",refresh_id"
     929              :            " FROM refresh"
     930              :            " JOIN known_coins kc"
     931              :            "   ON (refresh.old_coin_pub = kc.coin_pub)"
     932              :            " JOIN denominations denoms"
     933              :            "   USING (denominations_serial)"
     934              :            " WHERE old_coin_pub=$1"
     935              :            "   AND refresh_id=$2;");
     936            4 :   PREPARE (pg,
     937              :            "get_purse_deposit_by_coin_pub",
     938              :            "SELECT"
     939              :            " partner_base_url"
     940              :            ",pd.amount_with_fee"
     941              :            ",denoms.fee_deposit"
     942              :            ",denoms.denom_pub_hash"
     943              :            ",pd.purse_pub"
     944              :            ",kc.age_commitment_hash"
     945              :            ",pd.coin_sig"
     946              :            ",pd.purse_deposit_serial_id"
     947              :            ",pdes.refunded"
     948              :            " FROM purse_deposits pd"
     949              :            " LEFT JOIN partners"
     950              :            "   USING (partner_serial_id)"
     951              :            " JOIN purse_requests pr"
     952              :            "   USING (purse_pub)"
     953              :            " LEFT JOIN purse_decision pdes"
     954              :            "   USING (purse_pub)"
     955              :            " JOIN known_coins kc"
     956              :            "   ON (pd.coin_pub = kc.coin_pub)"
     957              :            " JOIN denominations denoms"
     958              :            "   USING (denominations_serial)"
     959              :            " WHERE pd.purse_deposit_serial_id=$2"
     960              :            "   AND pd.coin_pub=$1;");
     961            4 :   PREPARE (pg,
     962              :            "get_purse_decision_by_coin_pub",
     963              :            "SELECT"
     964              :            " pdes.purse_pub"
     965              :            ",pd.amount_with_fee"
     966              :            ",denom.fee_refund"
     967              :            ",pdes.purse_decision_serial_id"
     968              :            " FROM purse_decision pdes"
     969              :            " JOIN purse_deposits pd"
     970              :            "   USING (purse_pub)"
     971              :            " JOIN known_coins kc"
     972              :            "   ON (pd.coin_pub = kc.coin_pub)"
     973              :            " JOIN denominations denom"
     974              :            "   USING (denominations_serial)"
     975              :            " WHERE pd.coin_pub=$1"
     976              :            "   AND pdes.purse_decision_serial_id=$2"
     977              :            "   AND pdes.refunded;");
     978            4 :   PREPARE (pg,
     979              :            "get_refunds_by_coin",
     980              :            "SELECT"
     981              :            " bdep.merchant_pub"
     982              :            ",ref.merchant_sig"
     983              :            ",bdep.h_contract_terms"
     984              :            ",ref.rtransaction_id"
     985              :            ",ref.amount_with_fee"
     986              :            ",denom.fee_refund"
     987              :            ",ref.refund_serial_id"
     988              :            " FROM refunds ref"
     989              :            " JOIN coin_deposits cdep"
     990              :            "   ON (ref.coin_pub = cdep.coin_pub AND ref.batch_deposit_serial_id = cdep.batch_deposit_serial_id)"
     991              :            " JOIN batch_deposits bdep"
     992              :            "   ON (ref.batch_deposit_serial_id = bdep.batch_deposit_serial_id)"
     993              :            " JOIN known_coins kc"
     994              :            "   ON (ref.coin_pub = kc.coin_pub)"
     995              :            " JOIN denominations denom"
     996              :            "   USING (denominations_serial)"
     997              :            " WHERE ref.coin_pub=$1"
     998              :            "   AND ref.refund_serial_id=$2;");
     999            4 :   PREPARE (pg,
    1000              :            "recoup_by_old_coin",
    1001              :            "SELECT"
    1002              :            " coins.coin_pub"
    1003              :            ",rr.coin_sig"
    1004              :            ",rr.coin_blind"
    1005              :            ",rr.amount"
    1006              :            ",rr.recoup_timestamp"
    1007              :            ",denoms.denom_pub_hash"
    1008              :            ",coins.denom_sig"
    1009              :            ",rr.recoup_refresh_uuid"
    1010              :            " FROM recoup_refresh rr"
    1011              :            " JOIN known_coins coins"
    1012              :            "   USING (coin_pub)"
    1013              :            " JOIN denominations denoms"
    1014              :            "   USING (denominations_serial)"
    1015              :            " WHERE recoup_refresh_uuid=$2"
    1016              :            "   AND refresh_id IN"
    1017              :            "   (SELECT refresh_id"
    1018              :            "    FROM refresh"
    1019              :            "    WHERE refresh.old_coin_pub=$1);");
    1020            4 :   PREPARE (pg,
    1021              :            "recoup_by_coin",
    1022              :            "SELECT"
    1023              :            " res.reserve_pub"
    1024              :            ",denoms.denom_pub_hash"
    1025              :            ",rcp.coin_sig"
    1026              :            ",rcp.coin_blind"
    1027              :            ",rcp.amount"
    1028              :            ",rcp.recoup_timestamp"
    1029              :            ",rcp.recoup_uuid"
    1030              :            " FROM recoup rcp"
    1031              :            " JOIN withdraw ro"
    1032              :            "   USING (withdraw_id)"
    1033              :            " JOIN reserves res"
    1034              :            "   USING (reserve_pub)"
    1035              :            " JOIN known_coins coins"
    1036              :            "   USING (coin_pub)"
    1037              :            " JOIN denominations denoms"
    1038              :            "   ON (denoms.denominations_serial = coins.denominations_serial)"
    1039              :            " WHERE rcp.recoup_uuid=$2"
    1040              :            "   AND coins.coin_pub=$1;");
    1041              :   /* Used to obtain recoup transactions
    1042              :      for a refreshed coin */
    1043            4 :   PREPARE (pg,
    1044              :            "recoup_by_refreshed_coin",
    1045              :            "SELECT"
    1046              :            " old_coins.coin_pub AS old_coin_pub"
    1047              :            ",rr.coin_sig"
    1048              :            ",rr.coin_blind"
    1049              :            ",rr.amount"
    1050              :            ",rr.recoup_timestamp"
    1051              :            ",denoms.denom_pub_hash"
    1052              :            ",coins.denom_sig"
    1053              :            ",recoup_refresh_uuid"
    1054              :            " FROM recoup_refresh rr"
    1055              :            "    JOIN refresh rfc"
    1056              :            "      ON (rr.refresh_id = rfc.refresh_id)"
    1057              :            "    JOIN known_coins old_coins"
    1058              :            "      ON (rfc.old_coin_pub = old_coins.coin_pub)"
    1059              :            "    JOIN known_coins coins"
    1060              :            "      ON (rr.coin_pub = coins.coin_pub)"
    1061              :            "    JOIN denominations denoms"
    1062              :            "      ON (denoms.denominations_serial = coins.denominations_serial)"
    1063              :            " WHERE rr.recoup_refresh_uuid=$2"
    1064              :            "   AND coins.coin_pub=$1;");
    1065            4 :   PREPARE (pg,
    1066              :            "reserve_open_by_coin",
    1067              :            "SELECT"
    1068              :            " reserve_open_deposit_uuid"
    1069              :            ",coin_sig"
    1070              :            ",reserve_sig"
    1071              :            ",contribution"
    1072              :            " FROM reserves_open_deposits"
    1073              :            " WHERE coin_pub=$1"
    1074              :            "   AND reserve_open_deposit_uuid=$2;");
    1075            4 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1076              :               "  --- landed here 1\n");
    1077            4 :   for (unsigned int i = 0; i<RETRIES; i++)
    1078              :   {
    1079              :     enum GNUNET_DB_QueryStatus qs;
    1080              :     uint64_t end;
    1081            4 :     struct GNUNET_PQ_ResultSpec rs[] = {
    1082            4 :       GNUNET_PQ_result_spec_uint64 ("coin_history_serial_id",
    1083              :                                     &end),
    1084            4 :       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    1085              :                                             h_denom_pub),
    1086            4 :       TALER_PQ_RESULT_SPEC_AMOUNT ("remaining",
    1087              :                                    balance),
    1088              :       GNUNET_PQ_result_spec_end
    1089              :     };
    1090              : 
    1091            4 :     if (begin_transaction)
    1092              :     {
    1093            4 :       if (GNUNET_OK !=
    1094            4 :           TALER_TALER_EXCHANGEDB_start_read_committed (pg,
    1095              :                                                        "get-coin-transactions"))
    1096              :       {
    1097            0 :         GNUNET_break (0);
    1098            4 :         return GNUNET_DB_STATUS_HARD_ERROR;
    1099              :       }
    1100              :     }
    1101              :     /* First only check the last item, to see if
    1102              :        we even need to iterate */
    1103            4 :     qs = GNUNET_PQ_eval_prepared_singleton_select (
    1104              :       pg->conn,
    1105              :       "get_coin_history_etag_balance",
    1106              :       params,
    1107              :       rs);
    1108            4 :     switch (qs)
    1109              :     {
    1110            0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    1111            0 :       if (begin_transaction)
    1112            0 :         TALER_EXCHANGEDB_rollback (pg);
    1113            0 :       return qs;
    1114            0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    1115            0 :       if (begin_transaction)
    1116            0 :         TALER_EXCHANGEDB_rollback (pg);
    1117            0 :       continue;
    1118            0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1119            0 :       if (begin_transaction)
    1120            0 :         TALER_EXCHANGEDB_rollback (pg);
    1121            0 :       return qs;
    1122            4 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1123            4 :       *etag_out = end;
    1124            4 :       if (end == etag_in)
    1125            0 :         return qs;
    1126              :     }
    1127              :     /* We indeed need to iterate over the history */
    1128            4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1129              :                 "Current ETag for coin %s is %llu\n",
    1130              :                 TALER_B2S (coin_pub),
    1131              :                 (unsigned long long) end);
    1132              : 
    1133            4 :     qs = GNUNET_PQ_eval_prepared_multi_select (
    1134              :       pg->conn,
    1135              :       "get_coin_history",
    1136              :       lparams,
    1137              :       &handle_history_entry,
    1138              :       &chc);
    1139            4 :     switch (qs)
    1140              :     {
    1141            0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    1142            0 :       if (begin_transaction)
    1143            0 :         TALER_EXCHANGEDB_rollback (pg);
    1144            0 :       return qs;
    1145            0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    1146            0 :       if (begin_transaction)
    1147            0 :         TALER_EXCHANGEDB_rollback (pg);
    1148            0 :       continue;
    1149            4 :     default:
    1150            4 :       break;
    1151              :     }
    1152            4 :     if (chc.failed)
    1153              :     {
    1154            0 :       if (begin_transaction)
    1155            0 :         TALER_EXCHANGEDB_rollback (pg);
    1156            0 :       TALER_EXCHANGEDB_free_coin_transaction_list (chc.head);
    1157            0 :       return GNUNET_DB_STATUS_SOFT_ERROR;
    1158              :     }
    1159            4 :     if (! begin_transaction)
    1160              :     {
    1161            0 :       *tlp = chc.head;
    1162            0 :       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1163              :     }
    1164            4 :     qs = TALER_EXCHANGEDB_commit (pg);
    1165            4 :     switch (qs)
    1166              :     {
    1167            0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    1168            0 :       TALER_EXCHANGEDB_free_coin_transaction_list (chc.head);
    1169            0 :       chc.head = NULL;
    1170            0 :       return qs;
    1171            0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    1172            0 :       TALER_EXCHANGEDB_free_coin_transaction_list (chc.head);
    1173            0 :       chc.head = NULL;
    1174            0 :       continue;
    1175            4 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1176              :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1177            4 :       *tlp = chc.head;
    1178            4 :       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1179              :     }
    1180              :   }
    1181            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1182              :               "  --- landed here 2\n");
    1183            0 :   return GNUNET_DB_STATUS_SOFT_ERROR;
    1184              : }
        

Generated by: LCOV version 2.0-1