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

Generated by: LCOV version 2.0-1