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

            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_contracts.c
      18              :  * @brief tests for the exchangedb functions whose primary table is
      19              :  *        `contracts`
      20              :  * @author Christian Grothoff
      21              :  *
      22              :  * Covers #TALER_EXCHANGEDB_insert_contract(),
      23              :  * #TALER_EXCHANGEDB_get_contract() and
      24              :  * #TALER_EXCHANGEDB_get_contract_by_purse().
      25              :  *
      26              :  * `contracts` references `purse_requests`: the encrypted contract belongs
      27              :  * to a purse.  It is fetched either by the purse or by the Diffie-Hellman
      28              :  * public key the client derived, which is why there are two getters.  A
      29              :  * second, different contract for the same purse is reported through
      30              :  * @e in_conflict rather than as an error.
      31              :  */
      32              : #include "test_common.h"
      33              : #include "exchange-database/get_contract.h"
      34              : #include "exchange-database/get_contract_by_purse.h"
      35              : #include "exchange-database/insert_contract.h"
      36              : 
      37              : 
      38              : /**
      39              :  * Build a timestamp from a number of seconds since the epoch.
      40              :  *
      41              :  * @param secs seconds since the epoch
      42              :  * @return the timestamp
      43              :  */
      44              : static struct GNUNET_TIME_Timestamp
      45            3 : ts (uint64_t secs)
      46              : {
      47            3 :   struct GNUNET_TIME_Absolute abs = {
      48            3 :     .abs_value_us = secs * 1000LLU * 1000LLU
      49              :   };
      50              : 
      51            3 :   return GNUNET_TIME_absolute_to_timestamp (abs);
      52              : }
      53              : 
      54              : 
      55              : /**
      56              :  * Build an encrypted contract.
      57              :  *
      58              :  * @param seed seed for the key, signature and payload
      59              :  * @param payload contents of the "encrypted" contract
      60              :  * @param[out] econtract set to the contract; free @e econtract with
      61              :  *        GNUNET_free()
      62              :  */
      63              : static void
      64            3 : make_econtract (uint32_t seed,
      65              :                 const char *payload,
      66              :                 struct TALER_EncryptedContract *econtract)
      67              : {
      68            3 :   memset (econtract,
      69              :           0,
      70              :           sizeof (*econtract));
      71            3 :   TDB_fill (&econtract->contract_pub,
      72              :             sizeof (econtract->contract_pub),
      73              :             seed);
      74            3 :   TDB_fill (&econtract->econtract_sig,
      75              :             sizeof (econtract->econtract_sig),
      76              :             seed);
      77            3 :   econtract->econtract_size = strlen (payload) + 1;
      78            3 :   econtract->econtract = GNUNET_memdup (payload,
      79              :                                         econtract->econtract_size);
      80            3 : }
      81              : 
      82              : 
      83              : /**
      84              :  * Nothing is known while the table is empty, and a contract for a purse
      85              :  * that does not exist is not stored.
      86              :  *
      87              :  * @param pg the database context
      88              :  * @return 0 on success
      89              :  */
      90              : static int
      91            1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
      92              : {
      93              :   struct TALER_PurseContractPublicKeyP purse_pub;
      94              :   struct TALER_PurseContractSignatureP econtract_sig;
      95              :   struct TALER_ContractDiffiePublicP pub_ckey;
      96              :   struct TALER_EncryptedContract econtract;
      97              :   size_t econtract_size;
      98            1 :   void *econtract_data = NULL;
      99              : 
     100            1 :   TDB_FILL (purse_pub,
     101              :             1);
     102            1 :   TDB_FILL (pub_ckey,
     103              :             1);
     104            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     105              :           TALER_EXCHANGEDB_get_contract (pg,
     106              :                                          &pub_ckey,
     107              :                                          &purse_pub,
     108              :                                          &econtract_sig,
     109              :                                          &econtract_size,
     110              :                                          &econtract_data));
     111            1 :   FAILIF (NULL != econtract_data);
     112            1 :   memset (&econtract,
     113              :           0,
     114              :           sizeof (econtract));
     115            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     116              :           TALER_EXCHANGEDB_get_contract_by_purse (pg,
     117              :                                                   &purse_pub,
     118              :                                                   &econtract));
     119            1 :   return 0;
     120              : }
     121              : 
     122              : 
     123              : /**
     124              :  * A contract is stored and found by both keys.
     125              :  *
     126              :  * @param pg the database context
     127              :  * @return 0 on success
     128              :  */
     129              : static int
     130            1 : check_insert_and_lookup (struct TALER_EXCHANGEDB_PostgresContext *pg)
     131              : {
     132              :   struct TDB_Purse purse;
     133              :   struct TALER_EncryptedContract econtract;
     134              :   struct TALER_EncryptedContract got;
     135              :   struct TALER_PurseContractPublicKeyP got_purse;
     136              :   struct TALER_PurseContractSignatureP got_sig;
     137              :   size_t got_size;
     138            1 :   void *got_data = NULL;
     139            1 :   bool in_conflict = true;
     140              : 
     141            1 :   TDB_purse (pg,
     142              :              10,
     143              :              "5",
     144              :              ts (1700000000),
     145              :              &purse);
     146            1 :   make_econtract (10,
     147              :                   "the-contract",
     148              :                   &econtract);
     149            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     150              :             TALER_EXCHANGEDB_insert_contract (pg,
     151              :                                               &purse.purse_pub,
     152              :                                               &econtract,
     153              :                                               &in_conflict),
     154              :             GNUNET_free (econtract.econtract));
     155            1 :   FAILIF_C (in_conflict,
     156              :             GNUNET_free (econtract.econtract));
     157            1 :   FAILIF_C (1 != TDB_count (pg,
     158              :                             "FROM contracts"),
     159              :             GNUNET_free (econtract.econtract));
     160              : 
     161              :   /* by the Diffie-Hellman key the client derived */
     162            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     163              :             TALER_EXCHANGEDB_get_contract (pg,
     164              :                                            &econtract.contract_pub,
     165              :                                            &got_purse,
     166              :                                            &got_sig,
     167              :                                            &got_size,
     168              :                                            &got_data),
     169              :             GNUNET_free (econtract.econtract));
     170            1 :   FAILIF_C (0 != GNUNET_memcmp (&got_purse,
     171              :                                 &purse.purse_pub),
     172              :             GNUNET_free (got_data); GNUNET_free (econtract.econtract));
     173            1 :   FAILIF_C (0 != GNUNET_memcmp (&got_sig,
     174              :                                 &econtract.econtract_sig),
     175              :             GNUNET_free (got_data); GNUNET_free (econtract.econtract));
     176            1 :   FAILIF_C (got_size != econtract.econtract_size,
     177              :             GNUNET_free (got_data); GNUNET_free (econtract.econtract));
     178            1 :   FAILIF_C (0 != memcmp (got_data,
     179              :                          econtract.econtract,
     180              :                          got_size),
     181              :             GNUNET_free (got_data); GNUNET_free (econtract.econtract));
     182            1 :   GNUNET_free (got_data);
     183              : 
     184              :   /* and by the purse */
     185            1 :   memset (&got,
     186              :           0,
     187              :           sizeof (got));
     188            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     189              :             TALER_EXCHANGEDB_get_contract_by_purse (pg,
     190              :                                                     &purse.purse_pub,
     191              :                                                     &got),
     192              :             GNUNET_free (econtract.econtract));
     193            1 :   FAILIF_C (0 != GNUNET_memcmp (&got.contract_pub,
     194              :                                 &econtract.contract_pub),
     195              :             GNUNET_free (got.econtract); GNUNET_free (econtract.econtract));
     196            1 :   FAILIF_C (got.econtract_size != econtract.econtract_size,
     197              :             GNUNET_free (got.econtract); GNUNET_free (econtract.econtract));
     198            1 :   FAILIF_C (0 != memcmp (got.econtract,
     199              :                          econtract.econtract,
     200              :                          got.econtract_size),
     201              :             GNUNET_free (got.econtract); GNUNET_free (econtract.econtract));
     202            1 :   GNUNET_free (got.econtract);
     203              : 
     204              :   /* re-inserting the identical contract is a no-op */
     205            1 :   in_conflict = true;
     206            1 :   FAILIF_C (0 >
     207              :             TALER_EXCHANGEDB_insert_contract (pg,
     208              :                                               &purse.purse_pub,
     209              :                                               &econtract,
     210              :                                               &in_conflict),
     211              :             GNUNET_free (econtract.econtract));
     212            1 :   FAILIF_C (1 != TDB_count (pg,
     213              :                             "FROM contracts"),
     214              :             GNUNET_free (econtract.econtract));
     215            1 :   GNUNET_free (econtract.econtract);
     216            1 :   return 0;
     217              : }
     218              : 
     219              : 
     220              : /**
     221              :  * A different contract for the same purse is a conflict.
     222              :  *
     223              :  * @param pg the database context
     224              :  * @return 0 on success
     225              :  */
     226              : static int
     227            1 : check_conflict (struct TALER_EXCHANGEDB_PostgresContext *pg)
     228              : {
     229              :   struct TDB_Purse purse;
     230              :   struct TALER_EncryptedContract econtract;
     231            1 :   bool in_conflict = false;
     232              : 
     233            1 :   TDB_purse (pg,
     234              :              10,
     235              :              "5",
     236              :              ts (1700000000),
     237              :              &purse);
     238            1 :   make_econtract (11,
     239              :                   "another-contract",
     240              :                   &econtract);
     241            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     242              :             TALER_EXCHANGEDB_insert_contract (pg,
     243              :                                               &purse.purse_pub,
     244              :                                               &econtract,
     245              :                                               &in_conflict),
     246              :             GNUNET_free (econtract.econtract));
     247            1 :   FAILIF_C (! in_conflict,
     248              :             GNUNET_free (econtract.econtract));
     249            1 :   FAILIF_C (1 != TDB_count (pg,
     250              :                             "FROM contracts"),
     251              :             GNUNET_free (econtract.econtract));
     252            1 :   GNUNET_free (econtract.econtract);
     253            1 :   return 0;
     254              : }
     255              : 
     256              : 
     257              : /**
     258              :  * A second purse can have its own contract, and the two do not mix.
     259              :  *
     260              :  * @param pg the database context
     261              :  * @return 0 on success
     262              :  */
     263              : static int
     264            1 : check_second_purse (struct TALER_EXCHANGEDB_PostgresContext *pg)
     265              : {
     266              :   struct TDB_Purse purse;
     267              :   struct TALER_EncryptedContract econtract;
     268              :   struct TALER_EncryptedContract got;
     269            1 :   bool in_conflict = true;
     270              : 
     271            1 :   TDB_purse (pg,
     272              :              12,
     273              :              "3",
     274              :              ts (1700000000),
     275              :              &purse);
     276            1 :   make_econtract (12,
     277              :                   "second-contract",
     278              :                   &econtract);
     279            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     280              :             TALER_EXCHANGEDB_insert_contract (pg,
     281              :                                               &purse.purse_pub,
     282              :                                               &econtract,
     283              :                                               &in_conflict),
     284              :             GNUNET_free (econtract.econtract));
     285            1 :   FAILIF_C (in_conflict,
     286              :             GNUNET_free (econtract.econtract));
     287            1 :   FAILIF_C (2 != TDB_count (pg,
     288              :                             "FROM contracts"),
     289              :             GNUNET_free (econtract.econtract));
     290            1 :   memset (&got,
     291              :           0,
     292              :           sizeof (got));
     293            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     294              :             TALER_EXCHANGEDB_get_contract_by_purse (pg,
     295              :                                                     &purse.purse_pub,
     296              :                                                     &got),
     297              :             GNUNET_free (econtract.econtract));
     298            1 :   FAILIF_C (0 != GNUNET_memcmp (&got.contract_pub,
     299              :                                 &econtract.contract_pub),
     300              :             GNUNET_free (got.econtract); GNUNET_free (econtract.econtract));
     301            1 :   GNUNET_free (got.econtract);
     302            1 :   GNUNET_free (econtract.econtract);
     303            1 :   return 0;
     304              : }
     305              : 
     306              : 
     307              : /**
     308              :  * The checks to run, in order.
     309              :  */
     310              : static const struct TDB_Test tests[] = {
     311              :   { "contracts-empty",
     312              :     &check_empty },
     313              :   { "contracts-insert-and-lookup",
     314              :     &check_insert_and_lookup },
     315              :   { "contracts-conflict",
     316              :     &check_conflict },
     317              :   { "contracts-second-purse",
     318              :     &check_second_purse },
     319              :   { NULL, NULL }
     320              : };
     321              : 
     322              : 
     323              : int
     324            1 : main (int argc,
     325              :       char *const *argv)
     326              : {
     327            1 :   return TDB_main (argc,
     328              :                    argv,
     329              :                    "test-contracts",
     330              :                    "Tests for the exchangedb `contracts' table",
     331              :                    tests);
     332              : }
     333              : 
     334              : 
     335              : /* end of test_contracts.c */
        

Generated by: LCOV version 2.0-1