LCOV - code coverage report
Current view: top level - exchangedb - test_purse_deposits.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 96.9 % 131 127
Test Date: 2026-09-11 18:55:36 Functions: 100.0 % 10 10

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2026 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 exchangedb/test_purse_deposits.c
      18              :  * @brief tests for the exchangedb functions whose primary table is
      19              :  *        `purse_deposits`
      20              :  * @author Christian Grothoff
      21              :  *
      22              :  * Covers #TALER_EXCHANGEDB_do_purse_deposit(),
      23              :  * #TALER_EXCHANGEDB_get_purse_deposit(),
      24              :  * #TALER_EXCHANGEDB_iterate_purse_deposits_above_serial_id() and
      25              :  * #TALER_EXCHANGEDB_iterate_purse_deposits_by_purse().
      26              :  *
      27              :  * `purse_deposits` references `purse_requests` and `known_coins`, which
      28              :  * TDB_purse() and TDB_coin() create.  do_purse_deposit() declines in three
      29              :  * ways: the coin has too little left, the purse has already been decided
      30              :  * (too late), and the same coin was already paid into the purse with a
      31              :  * different amount (conflict).
      32              :  */
      33              : #include "test_common.h"
      34              : #include "exchange-database/do_expire_purse.h"
      35              : #include "exchange-database/do_purse_deposit.h"
      36              : #include "exchange-database/get_purse_deposit.h"
      37              : #include "exchange-database/iterate_purse_deposits_above_serial_id.h"
      38              : #include "exchange-database/iterate_purse_deposits_by_purse.h"
      39              : #include "exchange-database/rollback.h"
      40              : #include "exchange-database/start.h"
      41              : 
      42              : 
      43              : /**
      44              :  * Denomination the checks deposit.
      45              :  */
      46              : static struct TDB_Denom denom;
      47              : 
      48              : 
      49              : /**
      50              :  * Build a timestamp from a number of seconds since the epoch.
      51              :  *
      52              :  * @param secs seconds since the epoch
      53              :  * @return the timestamp
      54              :  */
      55              : static struct GNUNET_TIME_Timestamp
      56            3 : ts (uint64_t secs)
      57              : {
      58            3 :   struct GNUNET_TIME_Absolute abs = {
      59            3 :     .abs_value_us = secs * 1000LLU * 1000LLU
      60              :   };
      61              : 
      62            3 :   return GNUNET_TIME_absolute_to_timestamp (abs);
      63              : }
      64              : 
      65              : 
      66              : /**
      67              :  * Closure for #deposit_cb() and #refund_cb().
      68              :  */
      69              : struct DepositContext
      70              : {
      71              :   /**
      72              :    * How many rows did the callback see?
      73              :    */
      74              :   unsigned int total;
      75              : 
      76              :   /**
      77              :    * Stop after this many rows; 0 for no limit.
      78              :    */
      79              :   unsigned int stop_after;
      80              : 
      81              :   /**
      82              :    * Coin we are looking for, NULL to match nothing.
      83              :    */
      84              :   const struct TALER_CoinSpendPublicKeyP *coin_pub;
      85              : 
      86              :   /**
      87              :    * How many times did we see it?
      88              :    */
      89              :   unsigned int matched;
      90              : 
      91              :   /**
      92              :    * Amount reported for it.
      93              :    */
      94              :   struct TALER_Amount amount;
      95              : 
      96              :   /**
      97              :    * Was a reserve reported for its purse?
      98              :    */
      99              :   bool have_reserve;
     100              : };
     101              : 
     102              : 
     103              : /**
     104              :  * Callback for
     105              :  * #TALER_EXCHANGEDB_iterate_purse_deposits_above_serial_id().
     106              :  *
     107              :  * @param cls a `struct DepositContext *`
     108              :  * @param rowid row of the deposit
     109              :  * @param deposit details of the deposit
     110              :  * @param reserve_pub reserve the purse was merged into, NULL if none
     111              :  * @param flags flags of the purse
     112              :  * @param auditor_balance balance of the purse
     113              :  * @param purse_total target amount of the purse
     114              :  * @param denom_pub denomination of the coin
     115              :  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to stop
     116              :  */
     117              : static enum GNUNET_GenericReturnValue
     118            1 : deposit_cb (void *cls,
     119              :             uint64_t rowid,
     120              :             const struct TALER_EXCHANGEDB_PurseDeposit *deposit,
     121              :             const struct TALER_ReservePublicKeyP *reserve_pub,
     122              :             enum TALER_WalletAccountMergeFlags flags,
     123              :             const struct TALER_Amount *auditor_balance,
     124              :             const struct TALER_Amount *purse_total,
     125              :             const struct TALER_DenominationPublicKey *denom_pub)
     126              : {
     127            1 :   struct DepositContext *ctx = cls;
     128              : 
     129              :   (void) rowid;
     130              :   (void) flags;
     131              :   (void) auditor_balance;
     132              :   (void) purse_total;
     133              :   (void) denom_pub;
     134            1 :   ctx->total++;
     135            1 :   if ( (NULL != ctx->coin_pub) &&
     136            1 :        (0 == GNUNET_memcmp (&deposit->coin_pub,
     137              :                             ctx->coin_pub)) )
     138              :   {
     139            1 :     ctx->matched++;
     140            1 :     ctx->amount = deposit->amount;
     141            1 :     ctx->have_reserve = (NULL != reserve_pub);
     142              :   }
     143            1 :   if ( (0 != ctx->stop_after) &&
     144            0 :        (ctx->total >= ctx->stop_after) )
     145            0 :     return GNUNET_SYSERR;
     146            1 :   return GNUNET_OK;
     147              : }
     148              : 
     149              : 
     150              : /**
     151              :  * Callback for #TALER_EXCHANGEDB_iterate_purse_deposits_by_purse().
     152              :  *
     153              :  * @param cls a `struct DepositContext *`
     154              :  * @param rowid row of the deposit
     155              :  * @param amount_with_fee how much was paid in
     156              :  * @param coin_pub which coin paid
     157              :  * @param denom_pub denomination of that coin
     158              :  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to stop
     159              :  */
     160              : static enum GNUNET_GenericReturnValue
     161            1 : refund_cb (void *cls,
     162              :            uint64_t rowid,
     163              :            const struct TALER_Amount *amount_with_fee,
     164              :            const struct TALER_CoinSpendPublicKeyP *coin_pub,
     165              :            const struct TALER_DenominationPublicKey *denom_pub)
     166              : {
     167            1 :   struct DepositContext *ctx = cls;
     168              : 
     169              :   (void) rowid;
     170              :   (void) denom_pub;
     171            1 :   ctx->total++;
     172            1 :   if ( (NULL != ctx->coin_pub) &&
     173            1 :        (0 == GNUNET_memcmp (coin_pub,
     174              :                             ctx->coin_pub)) )
     175              :   {
     176            1 :     ctx->matched++;
     177            1 :     ctx->amount = *amount_with_fee;
     178              :   }
     179            1 :   if ( (0 != ctx->stop_after) &&
     180            0 :        (ctx->total >= ctx->stop_after) )
     181            0 :     return GNUNET_SYSERR;
     182            1 :   return GNUNET_OK;
     183              : }
     184              : 
     185              : 
     186              : /**
     187              :  * Outcome of a purse deposit.
     188              :  */
     189              : struct DepositStatus
     190              : {
     191              :   /**
     192              :    * Did the coin have enough left?
     193              :    */
     194              :   bool balance_ok;
     195              : 
     196              :   /**
     197              :    * Was the purse already decided?
     198              :    */
     199              :   bool too_late;
     200              : 
     201              :   /**
     202              :    * Was the coin already paid into this purse differently?
     203              :    */
     204              :   bool conflict;
     205              : };
     206              : 
     207              : 
     208              : /**
     209              :  * Pay a coin into a purse.
     210              :  *
     211              :  * @param pg the database context
     212              :  * @param purse purse to credit
     213              :  * @param coin coin to debit
     214              :  * @param seed seed for the coin signature
     215              :  * @param amount how much to take from the coin, e.g. "1"
     216              :  * @param fee purse deposit fee included in @a amount, e.g. "0"
     217              :  * @param[out] st set to the outcome
     218              :  * @return transaction status
     219              :  */
     220              : static enum GNUNET_DB_QueryStatus
     221            5 : run_deposit (struct TALER_EXCHANGEDB_PostgresContext *pg,
     222              :              const struct TDB_Purse *purse,
     223              :              const struct TALER_CoinPublicInfo *coin,
     224              :              uint32_t seed,
     225              :              const char *amount,
     226              :              const char *fee,
     227              :              struct DepositStatus *st)
     228              : {
     229              :   struct TALER_CoinSpendSignatureP coin_sig;
     230            5 :   struct TALER_Amount a = TDB_amount (amount);
     231            5 :   struct TALER_Amount f = TDB_amount (fee);
     232              :   struct TALER_Amount net;
     233              : 
     234            5 :   TDB_fill (&coin_sig,
     235              :             sizeof (coin_sig),
     236              :             seed);
     237            5 :   GNUNET_assert (0 <=
     238              :                  TALER_amount_subtract (&net,
     239              :                                         &a,
     240              :                                         &f));
     241            5 :   memset (st,
     242              :           0,
     243              :           sizeof (*st));
     244            5 :   return TALER_EXCHANGEDB_do_purse_deposit (pg,
     245              :                                             &purse->purse_pub,
     246              :                                             &coin->coin_pub,
     247              :                                             &a,
     248              :                                             &coin_sig,
     249              :                                             &net,
     250              :                                             &st->balance_ok,
     251              :                                             &st->too_late,
     252              :                                             &st->conflict);
     253              : }
     254              : 
     255              : 
     256              : /**
     257              :  * Nothing is reported while the table is empty.
     258              :  *
     259              :  * @param pg the database context
     260              :  * @return 0 on success
     261              :  */
     262              : static int
     263            1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
     264              : {
     265              :   struct TALER_PurseContractPublicKeyP purse_pub;
     266              :   struct TALER_CoinSpendPublicKeyP coin_pub;
     267              :   struct TALER_Amount amount;
     268              :   struct TALER_DenominationHashP h_denom_pub;
     269              :   struct TALER_AgeCommitmentHashP phac;
     270              :   struct TALER_CoinSpendSignatureP coin_sig;
     271            1 :   struct DepositContext ctx = { 0 };
     272            1 :   char *partner_url = NULL;
     273              :   bool no_age_commitment;
     274              : 
     275            1 :   TDB_denom (pg,
     276              :              10,
     277              :              "5",
     278              :              "0.1",
     279              :              &denom);
     280            1 :   TDB_FILL (purse_pub,
     281              :             1);
     282            1 :   TDB_FILL (coin_pub,
     283              :             1);
     284            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     285              :           TALER_EXCHANGEDB_get_purse_deposit (pg,
     286              :                                               &purse_pub,
     287              :                                               &coin_pub,
     288              :                                               &amount,
     289              :                                               &h_denom_pub,
     290              :                                               &phac,
     291              :                                               &no_age_commitment,
     292              :                                               &coin_sig,
     293              :                                               &partner_url));
     294            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     295              :           TALER_EXCHANGEDB_iterate_purse_deposits_above_serial_id (
     296              :             pg,
     297              :             0,
     298              :             &deposit_cb,
     299              :             &ctx));
     300            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     301              :           TALER_EXCHANGEDB_iterate_purse_deposits_by_purse (pg,
     302              :                                                             &purse_pub,
     303              :                                                             &refund_cb,
     304              :                                                             &ctx));
     305            1 :   FAILIF (0 != ctx.total);
     306            1 :   return 0;
     307              : }
     308              : 
     309              : 
     310              : /**
     311              :  * Paying a coin into a purse debits the coin and credits the purse.
     312              :  *
     313              :  * @param pg the database context
     314              :  * @return 0 on success
     315              :  */
     316              : static int
     317            1 : check_deposit (struct TALER_EXCHANGEDB_PostgresContext *pg)
     318              : {
     319              :   struct TDB_Purse purse;
     320              :   struct TALER_CoinPublicInfo coin;
     321              :   struct DepositStatus st;
     322              :   struct TALER_Amount amount;
     323            1 :   struct TALER_Amount expect = TDB_amount ("1");
     324              :   struct TALER_DenominationHashP h_denom_pub;
     325              :   struct TALER_AgeCommitmentHashP phac;
     326              :   struct TALER_CoinSpendSignatureP coin_sig;
     327              :   struct TALER_CoinSpendSignatureP expect_sig;
     328            1 :   char *partner_url = NULL;
     329            1 :   bool no_age_commitment = false;
     330              : 
     331            1 :   TDB_purse (pg,
     332              :              10,
     333              :              "5",
     334              :              ts (1700000000),
     335              :              &purse);
     336            1 :   TDB_coin (pg,
     337              :             &denom,
     338              :             20,
     339              :             &coin,
     340              :             NULL);
     341            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     342              :             run_deposit (pg,
     343              :                          &purse,
     344              :                          &coin,
     345              :                          20,
     346              :                          "1",
     347              :                          "0",
     348              :                          &st),
     349              :             TDB_coin_free (&coin));
     350            1 :   FAILIF_C (! st.balance_ok,
     351              :             TDB_coin_free (&coin));
     352            1 :   FAILIF_C (st.too_late,
     353              :             TDB_coin_free (&coin));
     354            1 :   FAILIF_C (st.conflict,
     355              :             TDB_coin_free (&coin));
     356            1 :   FAILIF_C (1 != TDB_count (pg,
     357              :                             "FROM purse_deposits"),
     358              :             TDB_coin_free (&coin));
     359              : 
     360              :   /* the coin was debited... */
     361              :   {
     362            1 :     char *hex = TDB_hex (&coin.coin_pub,
     363              :                          sizeof (coin.coin_pub));
     364              : 
     365            1 :     FAILIF_C (1 != TDB_count (pg,
     366              :                               "FROM known_coins"
     367              :                               " WHERE coin_pub=decode('%s','hex')"
     368              :                               "   AND remaining=ROW(4,0)::taler_amount",
     369              :                               hex),
     370              :               GNUNET_free (hex); TDB_coin_free (&coin));
     371            1 :     GNUNET_free (hex);
     372              :   }
     373              :   /* ...and the purse credited */
     374              :   {
     375            1 :     char *hex = TDB_hex (&purse.purse_pub,
     376              :                          sizeof (purse.purse_pub));
     377              : 
     378            1 :     FAILIF_C (1 != TDB_count (pg,
     379              :                               "FROM purse_requests"
     380              :                               " WHERE purse_pub=decode('%s','hex')"
     381              :                               "   AND balance=ROW(1,0)::taler_amount",
     382              :                               hex),
     383              :               GNUNET_free (hex); TDB_coin_free (&coin));
     384            1 :     GNUNET_free (hex);
     385              :   }
     386              : 
     387            1 :   TDB_FILL (expect_sig,
     388              :             20);
     389            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     390              :             TALER_EXCHANGEDB_get_purse_deposit (pg,
     391              :                                                 &purse.purse_pub,
     392              :                                                 &coin.coin_pub,
     393              :                                                 &amount,
     394              :                                                 &h_denom_pub,
     395              :                                                 &phac,
     396              :                                                 &no_age_commitment,
     397              :                                                 &coin_sig,
     398              :                                                 &partner_url),
     399              :             TDB_coin_free (&coin));
     400            1 :   FAILIF_C (0 != TALER_amount_cmp (&amount,
     401              :                                    &expect),
     402              :             GNUNET_free (partner_url); TDB_coin_free (&coin));
     403            1 :   FAILIF_C (0 != GNUNET_memcmp (&h_denom_pub,
     404              :                                 &denom.h_denom_pub),
     405              :             GNUNET_free (partner_url); TDB_coin_free (&coin));
     406            1 :   FAILIF_C (0 != GNUNET_memcmp (&coin_sig,
     407              :                                 &expect_sig),
     408              :             GNUNET_free (partner_url); TDB_coin_free (&coin));
     409            1 :   FAILIF_C (! no_age_commitment,
     410              :             GNUNET_free (partner_url); TDB_coin_free (&coin));
     411              :   /* the purse is ours, so there is no partner exchange */
     412            1 :   FAILIF_C (NULL != partner_url,
     413              :             GNUNET_free (partner_url); TDB_coin_free (&coin));
     414              : 
     415              :   /* re-depositing the same coin with the same amount is idempotent */
     416            1 :   FAILIF_C (0 >
     417              :             run_deposit (pg,
     418              :                          &purse,
     419              :                          &coin,
     420              :                          20,
     421              :                          "1",
     422              :                          "0",
     423              :                          &st),
     424              :             TDB_coin_free (&coin));
     425            1 :   FAILIF_C (1 != TDB_count (pg,
     426              :                             "FROM purse_deposits"),
     427              :             TDB_coin_free (&coin));
     428              : 
     429              :   /* ...but a second attempt with a different coin signature is a conflict:
     430              :      the idempotency check compares the signature, not the amount */
     431            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     432              :             run_deposit (pg,
     433              :                          &purse,
     434              :                          &coin,
     435              :                          29,
     436              :                          "1",
     437              :                          "0",
     438              :                          &st),
     439              :             TDB_coin_free (&coin));
     440            1 :   FAILIF_C (! st.conflict,
     441              :             TDB_coin_free (&coin));
     442            1 :   FAILIF_C (1 != TDB_count (pg,
     443              :                             "FROM purse_deposits"),
     444              :             TDB_coin_free (&coin));
     445            1 :   TDB_coin_free (&coin);
     446            1 :   return 0;
     447              : }
     448              : 
     449              : 
     450              : /**
     451              :  * A coin without enough left cannot be paid into a purse.
     452              :  *
     453              :  * @param pg the database context
     454              :  * @return 0 on success
     455              :  */
     456              : static int
     457            1 : check_insufficient_balance (struct TALER_EXCHANGEDB_PostgresContext *pg)
     458              : {
     459              :   struct TDB_Purse purse;
     460              :   struct TALER_CoinPublicInfo coin;
     461              :   struct DepositStatus st;
     462              : 
     463            1 :   TDB_purse (pg,
     464              :              10,
     465              :              "5",
     466              :              ts (1700000000),
     467              :              &purse);
     468            1 :   TDB_coin (pg,
     469              :             &denom,
     470              :             21,
     471              :             &coin,
     472              :             NULL);
     473              :   /* The `purse_deposits` row goes in before the coin balance is checked,
     474              :      so the caller has to roll back -- which is what the /purses/.../deposit
     475              :      handler does. */
     476            1 :   FAILIF_C (GNUNET_OK !=
     477              :             TALER_EXCHANGEDB_start (pg,
     478              :                                     "test-purse-deposit-insufficient"),
     479              :             TDB_coin_free (&coin));
     480            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     481              :             run_deposit (pg,
     482              :                          &purse,
     483              :                          &coin,
     484              :                          21,
     485              :                          "6",
     486              :                          "0",
     487              :                          &st),
     488              :             TALER_EXCHANGEDB_rollback (pg); TDB_coin_free (&coin));
     489            1 :   TALER_EXCHANGEDB_rollback (pg);
     490            1 :   TDB_coin_free (&coin);
     491            1 :   FAILIF (st.balance_ok);
     492            1 :   FAILIF (st.conflict);
     493            1 :   FAILIF (1 != TDB_count (pg,
     494              :                           "FROM purse_deposits"));
     495            1 :   return 0;
     496              : }
     497              : 
     498              : 
     499              : /**
     500              :  * Once a merged purse has expired and been decided, the deposit that
     501              :  * would complete it is refused as too late.
     502              :  *
     503              :  * Note what this shows about the shape of the function: the "too late"
     504              :  * answer only comes out of the attempt to record the decision, which is
     505              :  * only reached for a purse that has been merged *and* is completed by this
     506              :  * deposit.  A deposit into an expired purse that neither completes it nor
     507              :  * has been merged is accepted -- see EDBT-9 in bugs.txt.
     508              :  *
     509              :  * @param pg the database context
     510              :  * @return 0 on success
     511              :  */
     512              : static int
     513            1 : check_too_late (struct TALER_EXCHANGEDB_PostgresContext *pg)
     514              : {
     515              :   struct TDB_Purse purse;
     516              :   struct TALER_CoinPublicInfo coin;
     517              :   struct TALER_ReservePublicKeyP reserve_pub;
     518              :   struct DepositStatus st;
     519              :   struct GNUNET_TIME_Absolute expiration;
     520              : 
     521              :   /* a purse that expired in 2020, merged into a reserve */
     522            1 :   TDB_purse (pg,
     523              :              11,
     524              :              "5",
     525              :              ts (1600000000),
     526              :              &purse);
     527            1 :   TDB_reserve (pg,
     528              :                11,
     529              :                "0",
     530              :                &reserve_pub);
     531              :   {
     532            1 :     char *phex = TDB_hex (&purse.purse_pub,
     533              :                           sizeof (purse.purse_pub));
     534            1 :     char *rhex = TDB_hex (&reserve_pub,
     535              :                           sizeof (reserve_pub));
     536              : 
     537            1 :     FAILIF_C (GNUNET_OK !=
     538              :               TDB_exec (pg,
     539              :                         "INSERT INTO purse_merges"
     540              :                         " (reserve_pub,purse_pub,merge_sig,merge_timestamp)"
     541              :                         " VALUES (decode('%s','hex'),decode('%s','hex')"
     542              :                         "        ,decode(repeat('11',64),'hex'),1000000);",
     543              :                         rhex,
     544              :                         phex),
     545              :               GNUNET_free (phex); GNUNET_free (rhex));
     546            1 :     GNUNET_free (phex);
     547            1 :     GNUNET_free (rhex);
     548              :   }
     549            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     550              :           TALER_EXCHANGEDB_do_expire_purse (pg,
     551              :                                             GNUNET_TIME_UNIT_ZERO_ABS,
     552              :                                             GNUNET_TIME_absolute_get (),
     553              :                                             &expiration));
     554            1 :   TDB_coin (pg,
     555              :             &denom,
     556              :             22,
     557              :             &coin,
     558              :             NULL);
     559              :   /* Pay in the full target amount: the "too late" answer only comes out
     560              :      when the purse would be decided by this deposit, which is when the
     561              :      function tries to insert the `purse_decision` row that already exists.
     562              :      As with the balance check, the row is written first, so roll back. */
     563            1 :   FAILIF_C (GNUNET_OK !=
     564              :             TALER_EXCHANGEDB_start (pg,
     565              :                                     "test-purse-deposit-too-late"),
     566              :             TDB_coin_free (&coin));
     567            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     568              :             run_deposit (pg,
     569              :                          &purse,
     570              :                          &coin,
     571              :                          22,
     572              :                          "5",
     573              :                          "0",
     574              :                          &st),
     575              :             TALER_EXCHANGEDB_rollback (pg); TDB_coin_free (&coin));
     576            1 :   TALER_EXCHANGEDB_rollback (pg);
     577            1 :   TDB_coin_free (&coin);
     578            1 :   FAILIF (! st.too_late);
     579            1 :   FAILIF (1 != TDB_count (pg,
     580              :                           "FROM purse_deposits"));
     581            1 :   return 0;
     582              : }
     583              : 
     584              : 
     585              : /**
     586              :  * The iterators see the deposit; the by-purse one only for its own purse.
     587              :  *
     588              :  * @param pg the database context
     589              :  * @return 0 on success
     590              :  */
     591              : static int
     592            1 : check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
     593              : {
     594              :   struct TALER_CoinSpendPublicKeyP coin_pub;
     595              :   struct TALER_PurseContractPublicKeyP purse_pub;
     596              :   struct DepositContext ctx;
     597            1 :   struct TALER_Amount expect = TDB_amount ("1");
     598              : 
     599            1 :   TDB_FILL (coin_pub,
     600              :             20);
     601            1 :   TDB_FILL (purse_pub,
     602              :             10);
     603            1 :   memset (&ctx,
     604              :           0,
     605              :           sizeof (ctx));
     606            1 :   ctx.coin_pub = &coin_pub;
     607            1 :   FAILIF (1 !=
     608              :           TALER_EXCHANGEDB_iterate_purse_deposits_above_serial_id (
     609              :             pg,
     610              :             0,
     611              :             &deposit_cb,
     612              :             &ctx));
     613            1 :   FAILIF (1 != ctx.matched);
     614            1 :   FAILIF (0 != TALER_amount_cmp (&ctx.amount,
     615              :                                  &expect));
     616              :   /* the purse was never merged, so there is no reserve */
     617            1 :   FAILIF (ctx.have_reserve);
     618              : 
     619            1 :   memset (&ctx,
     620              :           0,
     621              :           sizeof (ctx));
     622            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     623              :           TALER_EXCHANGEDB_iterate_purse_deposits_above_serial_id (
     624              :             pg,
     625              :             1000,
     626              :             &deposit_cb,
     627              :             &ctx));
     628            1 :   FAILIF (0 != ctx.total);
     629              : 
     630            1 :   memset (&ctx,
     631              :           0,
     632              :           sizeof (ctx));
     633            1 :   ctx.coin_pub = &coin_pub;
     634            1 :   FAILIF (1 !=
     635              :           TALER_EXCHANGEDB_iterate_purse_deposits_by_purse (pg,
     636              :                                                             &purse_pub,
     637              :                                                             &refund_cb,
     638              :                                                             &ctx));
     639            1 :   FAILIF (1 != ctx.matched);
     640            1 :   FAILIF (0 != TALER_amount_cmp (&ctx.amount,
     641              :                                  &expect));
     642              : 
     643              :   /* a purse nobody paid into has no coins */
     644              :   {
     645              :     struct TALER_PurseContractPublicKeyP other;
     646              : 
     647            1 :     TDB_FILL (other,
     648              :               11);
     649            1 :     memset (&ctx,
     650              :             0,
     651              :             sizeof (ctx));
     652            1 :     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     653              :             TALER_EXCHANGEDB_iterate_purse_deposits_by_purse (pg,
     654              :                                                               &other,
     655              :                                                               &refund_cb,
     656              :                                                               &ctx));
     657            1 :     FAILIF (0 != ctx.total);
     658              :   }
     659            1 :   return 0;
     660              : }
     661              : 
     662              : 
     663              : /**
     664              :  * The checks to run, in order.
     665              :  */
     666              : static const struct TDB_Test tests[] = {
     667              :   { "purse-deposits-empty",
     668              :     &check_empty },
     669              :   { "purse-deposits-deposit",
     670              :     &check_deposit },
     671              :   { "purse-deposits-insufficient-balance",
     672              :     &check_insufficient_balance },
     673              :   { "purse-deposits-too-late",
     674              :     &check_too_late },
     675              :   { "purse-deposits-iterate",
     676              :     &check_iterate },
     677              :   { NULL, NULL }
     678              : };
     679              : 
     680              : 
     681              : int
     682            1 : main (int argc,
     683              :       char *const *argv)
     684              : {
     685              :   int ret;
     686              : 
     687            1 :   ret = TDB_main (argc,
     688              :                   argv,
     689              :                   "test-purse-deposits",
     690              :                   "Tests for the exchangedb `purse_deposits' table",
     691              :                   tests);
     692            1 :   TDB_denom_free (&denom);
     693            1 :   return ret;
     694              : }
     695              : 
     696              : 
     697              : /* end of test_purse_deposits.c */
        

Generated by: LCOV version 2.0-1