LCOV - code coverage report
Current view: top level - exchangedb - test_known_coins.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 208 208
Test Date: 2026-10-10 21:00:14 Functions: 100.0 % 9 9

            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_known_coins.c
      18              :  * @brief tests for the exchangedb functions whose primary table is
      19              :  *        `known_coins`
      20              :  * @author Christian Grothoff
      21              :  *
      22              :  * Covers #TALER_EXCHANGEDB_do_insert_known_coins(),
      23              :  * #TALER_EXCHANGEDB_get_known_coin(),
      24              :  * #TALER_EXCHANGEDB_get_coin_denomination(),
      25              :  * #TALER_EXCHANGEDB_get_signature_for_known_coin() and
      26              :  * #TALER_EXCHANGEDB_get_count_known_coins().
      27              :  *
      28              :  * `known_coins` references `denominations`, so a denomination is created
      29              :  * first with TDB_denom().  do_insert_known_coins() is the interesting one:
      30              :  * it is idempotent, but only for a coin that comes back with the *same*
      31              :  * denomination and age commitment -- the conflicting cases are what its
      32              :  * negative per-coin status codes are for.  It takes a whole batch of
      33              :  * coins in one round trip, so the last check mixes all cases in one call
      34              :  * and verifies that every coin is reported at its own position.
      35              :  */
      36              : #include "test_common.h"
      37              : #include "exchange-database/do_insert_known_coins.h"
      38              : #include "exchange-database/get_known_coin.h"
      39              : #include "exchange-database/get_coin_denomination.h"
      40              : #include "exchange-database/get_signature_for_known_coin.h"
      41              : #include "exchange-database/get_count_known_coins.h"
      42              : #include "exchange-database/start.h"
      43              : #include "exchange-database/rollback.h"
      44              : 
      45              : 
      46              : /**
      47              :  * Make a single @a coin known via the batch function.
      48              :  *
      49              :  * @param pg the database context
      50              :  * @param coin the coin to make known
      51              :  * @param[out] res outcome for the coin
      52              :  * @return database status, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT on success
      53              :  */
      54              : static enum GNUNET_DB_QueryStatus
      55            8 : insert_one (struct TALER_EXCHANGEDB_PostgresContext *pg,
      56              :             const struct TALER_CoinPublicInfo *coin,
      57              :             struct TALER_EXCHANGEDB_CoinKnownResult *res)
      58              : {
      59            8 :   const struct TALER_CoinPublicInfo *coins[1] = { coin };
      60              : 
      61            8 :   return TALER_EXCHANGEDB_do_insert_known_coins (pg,
      62              :                                                  1,
      63              :                                                  coins,
      64              :                                                  res);
      65              : }
      66              : 
      67              : 
      68              : /**
      69              :  * Nothing is known about a coin that was never inserted.
      70              :  *
      71              :  * @param pg the database context
      72              :  * @return 0 on success
      73              :  */
      74              : static int
      75            1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
      76              : {
      77              :   struct TALER_CoinSpendPublicKeyP coin_pub;
      78              :   struct TALER_CoinPublicInfo info;
      79              :   struct TALER_DenominationHashP h_denom_pub;
      80            1 :   struct TALER_DenominationPublicKey denom_pub = { 0 };
      81            1 :   struct TALER_DenominationSignature denom_sig = { 0 };
      82              :   uint64_t known_coin_id;
      83              : 
      84            1 :   TDB_FILL (coin_pub,
      85              :             1);
      86            1 :   TDB_FILL (h_denom_pub,
      87              :             1);
      88            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
      89              :           TALER_EXCHANGEDB_get_known_coin (pg,
      90              :                                            &coin_pub,
      91              :                                            &info));
      92            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
      93              :           TALER_EXCHANGEDB_get_coin_denomination (pg,
      94              :                                                   &coin_pub,
      95              :                                                   &known_coin_id,
      96              :                                                   &h_denom_pub));
      97            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
      98              :           TALER_EXCHANGEDB_get_signature_for_known_coin (pg,
      99              :                                                          &coin_pub,
     100              :                                                          &denom_pub,
     101              :                                                          &denom_sig));
     102            1 :   FAILIF (NULL != denom_pub.bsign_pub_key);
     103            1 :   FAILIF (NULL != denom_sig.unblinded_sig);
     104              :   /* a denomination that does not exist has no coins */
     105            1 :   FAILIF (0 != TALER_EXCHANGEDB_get_count_known_coins (pg,
     106              :                                                        &h_denom_pub));
     107            1 :   return 0;
     108              : }
     109              : 
     110              : 
     111              : /**
     112              :  * A coin of an unknown denomination cannot be made known.
     113              :  *
     114              :  * @param pg the database context
     115              :  * @return 0 on success
     116              :  */
     117              : static int
     118            1 : check_unknown_denomination (struct TALER_EXCHANGEDB_PostgresContext *pg)
     119              : {
     120              :   struct TALER_CoinPublicInfo coin;
     121              :   struct TALER_EXCHANGEDB_CoinKnownResult res;
     122              : 
     123            1 :   memset (&coin,
     124              :           0,
     125              :           sizeof (coin));
     126            1 :   TDB_FILL (coin.coin_pub,
     127              :             2);
     128            1 :   TDB_FILL (coin.denom_pub_hash,
     129              :             2);
     130            1 :   coin.no_age_commitment = true;
     131            1 :   TDB_denom_sig (2,
     132              :                  &coin.denom_sig);
     133              :   /* no denomination row, so nothing is inserted and no coin is found
     134              :      either: the row for the coin has no known_coin_id, a hard error */
     135            1 :   FAILIF_C (GNUNET_DB_STATUS_HARD_ERROR !=
     136              :             insert_one (pg,
     137              :                         &coin,
     138              :                         &res),
     139              :             TALER_denom_sig_free (&coin.denom_sig));
     140            1 :   TALER_denom_sig_free (&coin.denom_sig);
     141            1 :   FAILIF (0 != TDB_count (pg,
     142              :                           "FROM known_coins"));
     143            1 :   return 0;
     144              : }
     145              : 
     146              : 
     147              : /**
     148              :  * Making a coin known stores it, and every lookup finds it.
     149              :  *
     150              :  * @param pg the database context
     151              :  * @return 0 on success
     152              :  */
     153              : static int
     154            1 : check_insert_and_lookup (struct TALER_EXCHANGEDB_PostgresContext *pg)
     155              : {
     156              :   struct TDB_Denom denom;
     157              :   struct TALER_CoinPublicInfo coin;
     158              :   struct TALER_CoinPublicInfo got;
     159              :   struct TALER_DenominationHashP h_denom_pub;
     160            1 :   struct TALER_DenominationPublicKey denom_pub = { 0 };
     161            1 :   struct TALER_DenominationSignature denom_sig = { 0 };
     162            1 :   uint64_t known_coin_id = 0;
     163            1 :   uint64_t id2 = 0;
     164              : 
     165            1 :   TDB_denom (pg,
     166              :              10,
     167              :              "5",
     168              :              "0.1",
     169              :              &denom);
     170            1 :   TDB_coin (pg,
     171              :             &denom,
     172              :             20,
     173              :             &coin,
     174              :             &known_coin_id);
     175            1 :   FAILIF_C (0 == known_coin_id,
     176              :             TDB_coin_free (&coin); TDB_denom_free (&denom));
     177              : 
     178            1 :   memset (&got,
     179              :           0,
     180              :           sizeof (got));
     181            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     182              :             TALER_EXCHANGEDB_get_known_coin (pg,
     183              :                                              &coin.coin_pub,
     184              :                                              &got),
     185              :             TDB_coin_free (&coin); TDB_denom_free (&denom));
     186            1 :   FAILIF_C (0 != GNUNET_memcmp (&got.denom_pub_hash,
     187              :                                 &denom.h_denom_pub),
     188              :             TALER_denom_sig_free (&got.denom_sig);
     189              :             TDB_coin_free (&coin); TDB_denom_free (&denom));
     190            1 :   FAILIF_C (! got.no_age_commitment,
     191              :             TALER_denom_sig_free (&got.denom_sig);
     192              :             TDB_coin_free (&coin); TDB_denom_free (&denom));
     193            1 :   FAILIF_C (0 != TALER_denom_sig_cmp (&got.denom_sig,
     194              :                                       &coin.denom_sig),
     195              :             TALER_denom_sig_free (&got.denom_sig);
     196              :             TDB_coin_free (&coin); TDB_denom_free (&denom));
     197            1 :   TALER_denom_sig_free (&got.denom_sig);
     198              : 
     199            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     200              :             TALER_EXCHANGEDB_get_coin_denomination (pg,
     201              :                                                     &coin.coin_pub,
     202              :                                                     &id2,
     203              :                                                     &h_denom_pub),
     204              :             TDB_coin_free (&coin); TDB_denom_free (&denom));
     205            1 :   FAILIF_C (id2 != known_coin_id,
     206              :             TDB_coin_free (&coin); TDB_denom_free (&denom));
     207            1 :   FAILIF_C (0 != GNUNET_memcmp (&h_denom_pub,
     208              :                                 &denom.h_denom_pub),
     209              :             TDB_coin_free (&coin); TDB_denom_free (&denom));
     210              : 
     211            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     212              :             TALER_EXCHANGEDB_get_signature_for_known_coin (pg,
     213              :                                                            &coin.coin_pub,
     214              :                                                            &denom_pub,
     215              :                                                            &denom_sig),
     216              :             TDB_coin_free (&coin); TDB_denom_free (&denom));
     217            1 :   FAILIF_C (0 != TALER_denom_pub_cmp (&denom_pub,
     218              :                                       &denom.pub),
     219              :             TALER_denom_pub_free (&denom_pub);
     220              :             TALER_denom_sig_free (&denom_sig);
     221              :             TDB_coin_free (&coin); TDB_denom_free (&denom));
     222            1 :   FAILIF_C (0 != TALER_denom_sig_cmp (&denom_sig,
     223              :                                       &coin.denom_sig),
     224              :             TALER_denom_pub_free (&denom_pub);
     225              :             TALER_denom_sig_free (&denom_sig);
     226              :             TDB_coin_free (&coin); TDB_denom_free (&denom));
     227            1 :   TALER_denom_pub_free (&denom_pub);
     228            1 :   TALER_denom_sig_free (&denom_sig);
     229              : 
     230            1 :   FAILIF_C (1 != TALER_EXCHANGEDB_get_count_known_coins (pg,
     231              :                                                          &denom.h_denom_pub),
     232              :             TDB_coin_free (&coin); TDB_denom_free (&denom));
     233            1 :   TDB_coin_free (&coin);
     234            1 :   TDB_denom_free (&denom);
     235            1 :   return 0;
     236              : }
     237              : 
     238              : 
     239              : /**
     240              :  * Re-inserting the same coin is a no-op; re-inserting it under a different
     241              :  * denomination or with an age commitment is a conflict.
     242              :  *
     243              :  * @param pg the database context
     244              :  * @return 0 on success
     245              :  */
     246              : static int
     247            1 : check_conflicts (struct TALER_EXCHANGEDB_PostgresContext *pg)
     248              : {
     249              :   struct TDB_Denom denom;
     250              :   struct TDB_Denom other;
     251              :   struct TALER_CoinPublicInfo coin;
     252              :   struct TALER_EXCHANGEDB_CoinKnownResult res;
     253              : 
     254            1 :   TDB_denom (pg,
     255              :              10,
     256              :              "5",
     257              :              "0.1",
     258              :              &denom);
     259            1 :   TDB_denom (pg,
     260              :              11,
     261              :              "5",
     262              :              "0.1",
     263              :              &other);
     264            1 :   memset (&coin,
     265              :           0,
     266              :           sizeof (coin));
     267            1 :   TDB_FILL (coin.coin_pub,
     268              :             20);
     269            1 :   coin.denom_pub_hash = denom.h_denom_pub;
     270            1 :   coin.no_age_commitment = true;
     271            1 :   TDB_denom_sig (20,
     272              :                  &coin.denom_sig);
     273              : 
     274              :   /* the coin from the previous check: already present, no conflict */
     275            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     276              :             insert_one (pg,
     277              :                         &coin,
     278              :                         &res),
     279              :             TALER_denom_sig_free (&coin.denom_sig);
     280              :             TDB_denom_free (&denom); TDB_denom_free (&other));
     281            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_PRESENT != res.status,
     282              :             TALER_denom_sig_free (&coin.denom_sig);
     283              :             TDB_denom_free (&denom); TDB_denom_free (&other));
     284            1 :   FAILIF_C (0 == res.known_coin_id,
     285              :             TALER_denom_sig_free (&coin.denom_sig);
     286              :             TDB_denom_free (&denom); TDB_denom_free (&other));
     287            1 :   FAILIF_C (! res.no_age_commitment,
     288              :             TALER_denom_sig_free (&coin.denom_sig);
     289              :             TDB_denom_free (&denom); TDB_denom_free (&other));
     290              : 
     291              :   /* same coin key, different denomination: conflict, and the stored
     292              :      denomination is handed back so the caller can report it */
     293            1 :   coin.denom_pub_hash = other.h_denom_pub;
     294            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     295              :             insert_one (pg,
     296              :                         &coin,
     297              :                         &res),
     298              :             TALER_denom_sig_free (&coin.denom_sig);
     299              :             TDB_denom_free (&denom); TDB_denom_free (&other));
     300            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_DENOM_CONFLICT != res.status,
     301              :             TALER_denom_sig_free (&coin.denom_sig);
     302              :             TDB_denom_free (&denom); TDB_denom_free (&other));
     303            1 :   FAILIF_C (0 != GNUNET_memcmp (&res.h_denom_pub,
     304              :                                 &denom.h_denom_pub),
     305              :             TALER_denom_sig_free (&coin.denom_sig);
     306              :             TDB_denom_free (&denom); TDB_denom_free (&other));
     307              : 
     308              :   /* same coin key and denomination, but now with an age commitment where
     309              :      the stored row has none: the caller should have passed none */
     310            1 :   coin.denom_pub_hash = denom.h_denom_pub;
     311            1 :   coin.no_age_commitment = false;
     312            1 :   TDB_FILL (coin.h_age_commitment,
     313              :             21);
     314            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     315              :             insert_one (pg,
     316              :                         &coin,
     317              :                         &res),
     318              :             TALER_denom_sig_free (&coin.denom_sig);
     319              :             TDB_denom_free (&denom); TDB_denom_free (&other));
     320            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NULL != res.status,
     321              :             TALER_denom_sig_free (&coin.denom_sig);
     322              :             TDB_denom_free (&denom); TDB_denom_free (&other));
     323            1 :   TALER_denom_sig_free (&coin.denom_sig);
     324              : 
     325              :   /* the conflicting attempts did not add anything */
     326            1 :   FAILIF_C (1 != TALER_EXCHANGEDB_get_count_known_coins (pg,
     327              :                                                          &denom.h_denom_pub),
     328              :             TDB_denom_free (&denom); TDB_denom_free (&other));
     329            1 :   FAILIF_C (0 != TALER_EXCHANGEDB_get_count_known_coins (pg,
     330              :                                                          &other.h_denom_pub),
     331              :             TDB_denom_free (&denom); TDB_denom_free (&other));
     332            1 :   TDB_denom_free (&denom);
     333            1 :   TDB_denom_free (&other);
     334            1 :   return 0;
     335              : }
     336              : 
     337              : 
     338              : /**
     339              :  * A coin with an age commitment round-trips, and the mirror-image
     340              :  * conflict is reported.
     341              :  *
     342              :  * @param pg the database context
     343              :  * @return 0 on success
     344              :  */
     345              : static int
     346            1 : check_age_commitment (struct TALER_EXCHANGEDB_PostgresContext *pg)
     347              : {
     348              :   struct TDB_Denom denom;
     349              :   struct TALER_CoinPublicInfo coin;
     350              :   struct TALER_CoinPublicInfo got;
     351              :   struct TALER_EXCHANGEDB_CoinKnownResult res;
     352              : 
     353            1 :   TDB_denom (pg,
     354              :              10,
     355              :              "5",
     356              :              "0.1",
     357              :              &denom);
     358            1 :   memset (&coin,
     359              :           0,
     360              :           sizeof (coin));
     361            1 :   TDB_FILL (coin.coin_pub,
     362              :             30);
     363            1 :   coin.denom_pub_hash = denom.h_denom_pub;
     364            1 :   coin.no_age_commitment = false;
     365            1 :   TDB_FILL (coin.h_age_commitment,
     366              :             31);
     367            1 :   TDB_denom_sig (30,
     368              :                  &coin.denom_sig);
     369            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     370              :             insert_one (pg,
     371              :                         &coin,
     372              :                         &res),
     373              :             TALER_denom_sig_free (&coin.denom_sig);
     374              :             TDB_denom_free (&denom));
     375            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_ADDED != res.status,
     376              :             TALER_denom_sig_free (&coin.denom_sig);
     377              :             TDB_denom_free (&denom));
     378            1 :   FAILIF_C (0 == res.known_coin_id,
     379              :             TALER_denom_sig_free (&coin.denom_sig);
     380              :             TDB_denom_free (&denom));
     381            1 :   memset (&got,
     382              :           0,
     383              :           sizeof (got));
     384            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     385              :             TALER_EXCHANGEDB_get_known_coin (pg,
     386              :                                              &coin.coin_pub,
     387              :                                              &got),
     388              :             TALER_denom_sig_free (&coin.denom_sig);
     389              :             TDB_denom_free (&denom));
     390            1 :   FAILIF_C (got.no_age_commitment,
     391              :             TALER_denom_sig_free (&got.denom_sig);
     392              :             TALER_denom_sig_free (&coin.denom_sig);
     393              :             TDB_denom_free (&denom));
     394            1 :   FAILIF_C (0 != GNUNET_memcmp (&got.h_age_commitment,
     395              :                                 &coin.h_age_commitment),
     396              :             TALER_denom_sig_free (&got.denom_sig);
     397              :             TALER_denom_sig_free (&coin.denom_sig);
     398              :             TDB_denom_free (&denom));
     399            1 :   TALER_denom_sig_free (&got.denom_sig);
     400              : 
     401              :   /* a different age commitment for the same coin is a conflict... */
     402            1 :   TDB_FILL (coin.h_age_commitment,
     403              :             32);
     404            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     405              :             insert_one (pg,
     406              :                         &coin,
     407              :                         &res),
     408              :             TALER_denom_sig_free (&coin.denom_sig);
     409              :             TDB_denom_free (&denom));
     410            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_AGE_CONFLICT_VALUE_DIFFERS != res.status,
     411              :             TALER_denom_sig_free (&coin.denom_sig);
     412              :             TDB_denom_free (&denom));
     413              :   /* the stored age commitment is handed back for the error report */
     414            1 :   FAILIF_C (res.no_age_commitment,
     415              :             TALER_denom_sig_free (&coin.denom_sig);
     416              :             TDB_denom_free (&denom));
     417            1 :   FAILIF_C (0 != GNUNET_memcmp (&res.h_age_commitment,
     418              :                                 &got.h_age_commitment),
     419              :             TALER_denom_sig_free (&coin.denom_sig);
     420              :             TDB_denom_free (&denom));
     421              :   /* ...and so is no age commitment at all, where one is on file */
     422            1 :   coin.no_age_commitment = true;
     423            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     424              :             insert_one (pg,
     425              :                         &coin,
     426              :                         &res),
     427              :             TALER_denom_sig_free (&coin.denom_sig);
     428              :             TDB_denom_free (&denom));
     429            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NON_NULL != res.status,
     430              :             TALER_denom_sig_free (&coin.denom_sig);
     431              :             TDB_denom_free (&denom));
     432            1 :   TALER_denom_sig_free (&coin.denom_sig);
     433              : 
     434              :   /* two coins of this denomination by now */
     435            1 :   FAILIF_C (2 != TALER_EXCHANGEDB_get_count_known_coins (pg,
     436              :                                                          &denom.h_denom_pub),
     437              :             TDB_denom_free (&denom));
     438            1 :   TDB_denom_free (&denom);
     439            1 :   return 0;
     440              : }
     441              : 
     442              : 
     443              : /**
     444              :  * One call with a mixed batch: two new coins (one with an age
     445              :  * commitment), one already known, one with a conflicting denomination
     446              :  * and one with a conflicting age commitment.  Every coin must be
     447              :  * reported at its own position, the new ones must be inserted, and a
     448              :  * batch with a repeated coin or an unknown denomination is refused
     449              :  * without inserting anything.
     450              :  *
     451              :  * @param pg the database context
     452              :  * @return 0 on success
     453              :  */
     454              : static int
     455            1 : check_batch (struct TALER_EXCHANGEDB_PostgresContext *pg)
     456              : {
     457              :   struct TDB_Denom denom;
     458              :   struct TDB_Denom other;
     459              :   struct TALER_CoinPublicInfo coins[5];
     460              :   const struct TALER_CoinPublicInfo *pcoins[5];
     461              :   struct TALER_EXCHANGEDB_CoinKnownResult res[5];
     462              :   struct TALER_DenominationHashP dh;
     463              :   uint64_t id;
     464              :   uint64_t count_before;
     465            1 :   int ret = 1;
     466              : 
     467            1 :   TDB_denom (pg,
     468              :              10,
     469              :              "5",
     470              :              "0.1",
     471              :              &denom);
     472            1 :   TDB_denom (pg,
     473              :              11,
     474              :              "5",
     475              :              "0.1",
     476              :              &other);
     477            1 :   memset (coins,
     478              :           0,
     479              :           sizeof (coins));
     480            6 :   for (unsigned int i = 0; i < 5; i++)
     481              :   {
     482            5 :     coins[i].no_age_commitment = true;
     483            5 :     coins[i].denom_pub_hash = denom.h_denom_pub;
     484            5 :     pcoins[i] = &coins[i];
     485              :   }
     486              :   /* [0]: new, no age commitment */
     487            1 :   TDB_FILL (coins[0].coin_pub,
     488              :             40);
     489            1 :   TDB_denom_sig (40,
     490              :                  &coins[0].denom_sig);
     491              :   /* [1]: new, with age commitment */
     492            1 :   TDB_FILL (coins[1].coin_pub,
     493              :             41);
     494            1 :   coins[1].no_age_commitment = false;
     495            1 :   TDB_FILL (coins[1].h_age_commitment,
     496              :             410);
     497            1 :   TDB_denom_sig (41,
     498              :                  &coins[1].denom_sig);
     499              :   /* [2]: known from check_insert_and_lookup (seed 20), same data */
     500            1 :   TDB_FILL (coins[2].coin_pub,
     501              :             20);
     502            1 :   TDB_denom_sig (20,
     503              :                  &coins[2].denom_sig);
     504              :   /* [3]: known (seed 20 again, different key below), other denomination */
     505            1 :   TDB_FILL (coins[3].coin_pub,
     506              :             20);
     507            1 :   coins[3].denom_pub_hash = other.h_denom_pub;
     508            1 :   TDB_denom_sig (20,
     509              :                  &coins[3].denom_sig);
     510              :   /* [4]: known from check_age_commitment (seed 30), wrong age commitment */
     511            1 :   TDB_FILL (coins[4].coin_pub,
     512              :             30);
     513            1 :   coins[4].no_age_commitment = false;
     514            1 :   TDB_FILL (coins[4].h_age_commitment,
     515              :             33);
     516            1 :   TDB_denom_sig (30,
     517              :                  &coins[4].denom_sig);
     518              : 
     519            1 :   count_before = TDB_count (pg,
     520              :                             "FROM known_coins");
     521              : 
     522              :   /* [2] and [3] share a key: refused, nothing inserted */
     523            1 :   FAILIF_C (GNUNET_DB_STATUS_HARD_ERROR !=
     524              :             TALER_EXCHANGEDB_do_insert_known_coins (pg,
     525              :                                                     5,
     526              :                                                     pcoins,
     527              :                                                     res),
     528              :             goto cleanup);
     529            1 :   FAILIF_C (count_before != TDB_count (pg,
     530              :                                        "FROM known_coins"),
     531              :             goto cleanup);
     532              : 
     533              :   /* give [3] its own key: a coin known under the other denomination */
     534              :   {
     535              :     struct TALER_EXCHANGEDB_CoinKnownResult r1;
     536            1 :     struct TALER_CoinPublicInfo tmp = coins[3];
     537              : 
     538            1 :     TDB_FILL (tmp.coin_pub,
     539              :               43);
     540            1 :     FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     541              :               insert_one (pg,
     542              :                           &tmp,
     543              :                           &r1),
     544              :               goto cleanup);
     545            1 :     FAILIF_C (TALER_EXCHANGEDB_CKS_ADDED != r1.status,
     546              :               goto cleanup);
     547            1 :     count_before++;
     548            1 :     TDB_FILL (coins[3].coin_pub,
     549              :               43);
     550            1 :     coins[3].denom_pub_hash = denom.h_denom_pub;
     551              :   }
     552              : 
     553              :   /* a new coin of an unknown denomination anywhere in the batch: the
     554              :      batch is refused.  The INSERT itself is one statement, so this is
     555              :      done inside a transaction as the callers do it, and the rollback
     556              :      removes the other new coin again. */
     557            1 :   TDB_FILL (coins[0].denom_pub_hash,
     558              :             44);
     559            1 :   FAILIF_C (GNUNET_OK !=
     560              :             TALER_EXCHANGEDB_start (pg,
     561              :                                     "test unknown denomination"),
     562              :             goto cleanup);
     563            1 :   FAILIF_C (GNUNET_DB_STATUS_HARD_ERROR !=
     564              :             TALER_EXCHANGEDB_do_insert_known_coins (pg,
     565              :                                                     5,
     566              :                                                     pcoins,
     567              :                                                     res),
     568              :             TALER_EXCHANGEDB_rollback (pg); goto cleanup);
     569            1 :   TALER_EXCHANGEDB_rollback (pg);
     570            1 :   FAILIF_C (count_before != TDB_count (pg,
     571              :                                        "FROM known_coins"),
     572              :             goto cleanup);
     573            1 :   coins[0].denom_pub_hash = denom.h_denom_pub;
     574              : 
     575              :   /* now the mixed batch goes through, with one row per coin */
     576            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     577              :             TALER_EXCHANGEDB_do_insert_known_coins (pg,
     578              :                                                     5,
     579              :                                                     pcoins,
     580              :                                                     res),
     581              :             goto cleanup);
     582            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_ADDED != res[0].status,
     583              :             goto cleanup);
     584            1 :   FAILIF_C (0 == res[0].known_coin_id,
     585              :             goto cleanup);
     586            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_ADDED != res[1].status,
     587              :             goto cleanup);
     588            1 :   FAILIF_C (0 == res[1].known_coin_id,
     589              :             goto cleanup);
     590              :   /* for added coins, the result reflects what was stored */
     591            1 :   FAILIF_C (! res[0].no_age_commitment,
     592              :             goto cleanup);
     593            1 :   FAILIF_C (0 != GNUNET_memcmp (&res[0].h_denom_pub,
     594              :                                 &denom.h_denom_pub),
     595              :             goto cleanup);
     596            1 :   FAILIF_C (res[1].no_age_commitment,
     597              :             goto cleanup);
     598            1 :   FAILIF_C (0 != GNUNET_memcmp (&res[1].h_age_commitment,
     599              :                                 &coins[1].h_age_commitment),
     600              :             goto cleanup);
     601            1 :   FAILIF_C (res[0].known_coin_id == res[1].known_coin_id,
     602              :             goto cleanup);
     603            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_PRESENT != res[2].status,
     604              :             goto cleanup);
     605            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     606              :             TALER_EXCHANGEDB_get_coin_denomination (pg,
     607              :                                                     &coins[2].coin_pub,
     608              :                                                     &id,
     609              :                                                     &dh),
     610              :             goto cleanup);
     611            1 :   FAILIF_C (id != res[2].known_coin_id,
     612              :             goto cleanup);
     613            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_DENOM_CONFLICT != res[3].status,
     614              :             goto cleanup);
     615            1 :   FAILIF_C (0 != GNUNET_memcmp (&res[3].h_denom_pub,
     616              :                                 &other.h_denom_pub),
     617              :             goto cleanup);
     618            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_AGE_CONFLICT_VALUE_DIFFERS != res[4].status,
     619              :             goto cleanup);
     620            1 :   FAILIF_C (res[4].no_age_commitment,
     621              :             goto cleanup);
     622            1 :   FAILIF_C (0 == GNUNET_memcmp (&res[4].h_age_commitment,
     623              :                                 &coins[4].h_age_commitment),
     624              :             goto cleanup);
     625              :   /* exactly the two new coins were added */
     626            1 :   FAILIF_C (count_before + 2 != TDB_count (pg,
     627              :                                            "FROM known_coins"),
     628              :             goto cleanup);
     629              :   {
     630              :     struct TALER_CoinPublicInfo got;
     631              : 
     632            1 :     memset (&got,
     633              :             0,
     634              :             sizeof (got));
     635            1 :     FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     636              :               TALER_EXCHANGEDB_get_known_coin (pg,
     637              :                                                &coins[1].coin_pub,
     638              :                                                &got),
     639              :               goto cleanup);
     640            1 :     FAILIF_C (got.no_age_commitment,
     641              :               TALER_denom_sig_free (&got.denom_sig); goto cleanup);
     642            1 :     FAILIF_C (0 != GNUNET_memcmp (&got.h_age_commitment,
     643              :                                   &coins[1].h_age_commitment),
     644              :               TALER_denom_sig_free (&got.denom_sig); goto cleanup);
     645            1 :     FAILIF_C (0 != TALER_denom_sig_cmp (&got.denom_sig,
     646              :                                         &coins[1].denom_sig),
     647              :               TALER_denom_sig_free (&got.denom_sig); goto cleanup);
     648            1 :     TALER_denom_sig_free (&got.denom_sig);
     649              :   }
     650              : 
     651              :   /* running the same batch again changes nothing: the new coins are
     652              :      now merely present, the conflicts are reported as before */
     653            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     654              :             TALER_EXCHANGEDB_do_insert_known_coins (pg,
     655              :                                                     5,
     656              :                                                     pcoins,
     657              :                                                     res),
     658              :             goto cleanup);
     659            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_PRESENT != res[0].status,
     660              :             goto cleanup);
     661            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_PRESENT != res[1].status,
     662              :             goto cleanup);
     663            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_PRESENT != res[2].status,
     664              :             goto cleanup);
     665            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_DENOM_CONFLICT != res[3].status,
     666              :             goto cleanup);
     667            1 :   FAILIF_C (TALER_EXCHANGEDB_CKS_AGE_CONFLICT_VALUE_DIFFERS != res[4].status,
     668              :             goto cleanup);
     669            1 :   FAILIF_C (count_before + 2 != TDB_count (pg,
     670              :                                            "FROM known_coins"),
     671              :             goto cleanup);
     672            1 :   ret = 0;
     673            1 : cleanup:
     674            6 :   for (unsigned int i = 0; i < 5; i++)
     675            5 :     TALER_denom_sig_free (&coins[i].denom_sig);
     676            1 :   TDB_denom_free (&denom);
     677            1 :   TDB_denom_free (&other);
     678            1 :   return ret;
     679              : }
     680              : 
     681              : 
     682              : /**
     683              :  * Both ways out of do_insert_known_coins() release the closures its
     684              :  * query parameters allocate.  Nothing to assert here: the leak shows up
     685              :  * when the test runs under valgrind (meson test --setup=valgrind).
     686              :  *
     687              :  * @param pg the database context
     688              :  * @return 0 on success
     689              :  */
     690              : static int
     691            1 : check_no_leak (struct TALER_EXCHANGEDB_PostgresContext *pg)
     692              : {
     693              :   struct TDB_Denom denom;
     694              :   struct TALER_CoinPublicInfo coins[2];
     695              :   const struct TALER_CoinPublicInfo *pcoins[2];
     696              :   const struct TALER_CoinPublicInfo *dups[2];
     697              :   struct TALER_EXCHANGEDB_CoinKnownResult res[2];
     698            1 :   int ret = 1;
     699              : 
     700            1 :   TDB_denom (pg,
     701              :              10,
     702              :              "5",
     703              :              "0.1",
     704              :              &denom);
     705            1 :   memset (coins,
     706              :           0,
     707              :           sizeof (coins));
     708            3 :   for (unsigned int i = 0; i < 2; i++)
     709              :   {
     710            2 :     coins[i].no_age_commitment = true;
     711            2 :     coins[i].denom_pub_hash = denom.h_denom_pub;
     712            2 :     TDB_FILL (coins[i].coin_pub,
     713              :               50 + i);
     714            2 :     TDB_denom_sig (50 + i,
     715              :                    &coins[i].denom_sig);
     716            2 :     pcoins[i] = &coins[i];
     717            2 :     dups[i] = &coins[0];
     718              :   }
     719              :   /* the batch goes through: first inserted, then found */
     720            3 :   for (unsigned int i = 0; i < 2; i++)
     721              :   {
     722            2 :     FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     723              :               TALER_EXCHANGEDB_do_insert_known_coins (pg,
     724              :                                                       2,
     725              :                                                       pcoins,
     726              :                                                       res),
     727              :               goto cleanup);
     728            2 :     FAILIF_C ( (i == 0)
     729              :                ? (TALER_EXCHANGEDB_CKS_ADDED != res[1].status)
     730              :                : (TALER_EXCHANGEDB_CKS_PRESENT != res[1].status),
     731              :                goto cleanup);
     732              :   }
     733              :   /* the batch is refused before the query for repeating a coin */
     734            1 :   FAILIF_C (GNUNET_DB_STATUS_HARD_ERROR !=
     735              :             TALER_EXCHANGEDB_do_insert_known_coins (pg,
     736              :                                                     2,
     737              :                                                     dups,
     738              :                                                     res),
     739              :             goto cleanup);
     740            1 :   ret = 0;
     741            1 : cleanup:
     742            3 :   for (unsigned int i = 0; i < 2; i++)
     743            2 :     TALER_denom_sig_free (&coins[i].denom_sig);
     744            1 :   TDB_denom_free (&denom);
     745            1 :   return ret;
     746              : }
     747              : 
     748              : 
     749              : /**
     750              :  * The checks to run, in order.
     751              :  */
     752              : static const struct TDB_Test tests[] = {
     753              :   { "known-coins-empty",
     754              :     &check_empty },
     755              :   { "known-coins-unknown-denomination",
     756              :     &check_unknown_denomination },
     757              :   { "known-coins-insert-and-lookup",
     758              :     &check_insert_and_lookup },
     759              :   { "known-coins-conflicts",
     760              :     &check_conflicts },
     761              :   { "known-coins-age-commitment",
     762              :     &check_age_commitment },
     763              :   { "known-coins-batch",
     764              :     &check_batch },
     765              :   { "known-coins-no-leak",
     766              :     &check_no_leak },
     767              :   { NULL, NULL }
     768              : };
     769              : 
     770              : 
     771              : int
     772            1 : main (int argc,
     773              :       char *const *argv)
     774              : {
     775            1 :   return TDB_main (argc,
     776              :                    argv,
     777              :                    "test-known-coins",
     778              :                    "Tests for the exchangedb `known_coins' table",
     779              :                    tests);
     780              : }
     781              : 
     782              : 
     783              : /* end of test_known_coins.c */
        

Generated by: LCOV version 2.0-1