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

            Line data    Source code
       1              : /*
       2              :    This file is part of TALER
       3              :    Copyright (C) 2022-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/do_insert_known_coins.c
      18              :  * @brief Implementation of the do_insert_known_coins function for Postgres
      19              :  * @author Christian Grothoff
      20              :  * @author Özgür Kesim
      21              :  */
      22              : #include "platform.h"
      23              : #include "taler/taler_error_codes.h"
      24              : #include "taler/taler_dbevents.h"
      25              : #include "exchangedb_lib.h"
      26              : #include "taler/taler_pq_lib.h"
      27              : #include "exchange-database/do_insert_known_coins.h"
      28              : #include "helper.h"
      29              : 
      30              : 
      31              : /**
      32              :  * Closure for #known_coins_cb().
      33              :  */
      34              : struct KnownCoinsContext
      35              : {
      36              :   /**
      37              :    * The coins we asked about.
      38              :    */
      39              :   const struct TALER_CoinPublicInfo *const *coins;
      40              : 
      41              :   /**
      42              :    * Where to put the per-coin outcome.
      43              :    */
      44              :   struct TALER_EXCHANGEDB_CoinKnownResult *results;
      45              : 
      46              :   /**
      47              :    * Number of entries in @e coins and @e results.
      48              :    */
      49              :   unsigned int num_coins;
      50              : 
      51              :   /**
      52              :    * Number of rows we have processed so far.
      53              :    */
      54              :   unsigned int num_rows;
      55              : 
      56              :   /**
      57              :    * Set to true if a row could not be processed.
      58              :    */
      59              :   bool failed;
      60              : };
      61              : 
      62              : 
      63              : /**
      64              :  * Function called with one row per coin.  Fills the
      65              :  * result entry at the coin's input position.
      66              :  *
      67              :  * @param cls a `struct KnownCoinsContext *`
      68              :  * @param result the result
      69              :  * @param num_results number of rows in @a result
      70              :  */
      71              : static void
      72          298 : known_coins_cb (void *cls,
      73              :                 PGresult *result,
      74              :                 unsigned int num_results)
      75              : {
      76          298 :   struct KnownCoinsContext *kcc = cls;
      77              : 
      78          612 :   for (unsigned int i = 0; i < num_results; i++)
      79              :   {
      80              :     uint64_t idx;
      81              :     bool existed;
      82              :     uint64_t known_coin_id;
      83              :     bool no_known_coin_id;
      84              :     struct TALER_DenominationHashP h_denom_pub;
      85              :     bool no_denom_pub_hash;
      86              :     struct TALER_AgeCommitmentHashP h_age_commitment;
      87              :     bool no_age_commitment_hash;
      88          316 :     struct GNUNET_PQ_ResultSpec rs[] = {
      89          316 :       GNUNET_PQ_result_spec_uint64 ("out_idx",
      90              :                                     &idx),
      91          316 :       GNUNET_PQ_result_spec_bool ("out_existed",
      92              :                                   &existed),
      93          316 :       GNUNET_PQ_result_spec_allow_null (
      94              :         GNUNET_PQ_result_spec_uint64 ("out_known_coin_id",
      95              :                                       &known_coin_id),
      96              :         &no_known_coin_id),
      97          316 :       GNUNET_PQ_result_spec_allow_null (
      98              :         GNUNET_PQ_result_spec_auto_from_type ("out_denom_pub_hash",
      99              :                                               &h_denom_pub),
     100              :         &no_denom_pub_hash),
     101          316 :       GNUNET_PQ_result_spec_allow_null (
     102              :         GNUNET_PQ_result_spec_auto_from_type ("out_age_commitment_hash",
     103              :                                               &h_age_commitment),
     104              :         &no_age_commitment_hash),
     105              :       GNUNET_PQ_result_spec_end
     106              :     };
     107              :     const struct TALER_CoinPublicInfo *coin;
     108              :     struct TALER_EXCHANGEDB_CoinKnownResult *res;
     109              : 
     110          316 :     if (GNUNET_OK !=
     111          316 :         GNUNET_PQ_extract_result (result,
     112              :                                   rs,
     113              :                                   i))
     114              :     {
     115            0 :       GNUNET_break (0);
     116            0 :       kcc->failed = true;
     117            2 :       return;
     118              :     }
     119              :     /* out_idx is the 1-based ordinality of the input arrays */
     120          316 :     if ( (0 == idx) ||
     121          316 :          (idx > kcc->num_coins) )
     122              :     {
     123            0 :       GNUNET_break (0);
     124            0 :       kcc->failed = true;
     125            0 :       return;
     126              :     }
     127          316 :     coin = kcc->coins[idx - 1];
     128          316 :     res = &kcc->results[idx - 1];
     129          316 :     if (no_known_coin_id)
     130              :     {
     131              :       /* neither inserted nor found: the denomination is unknown */
     132            2 :       GNUNET_break (0);
     133            2 :       kcc->failed = true;
     134            2 :       return;
     135              :     }
     136          314 :     res->known_coin_id = known_coin_id;
     137          314 :     kcc->num_rows++;
     138          314 :     if (! existed)
     139              :     {
     140              :       /* The row was inserted by this very statement, which the
     141              :          query cannot see yet: what is stored is what we passed. */
     142          174 :       res->status = TALER_EXCHANGEDB_CKS_ADDED;
     143          174 :       res->h_denom_pub = coin->denom_pub_hash;
     144          174 :       res->no_age_commitment = coin->no_age_commitment;
     145          174 :       if (! coin->no_age_commitment)
     146           46 :         res->h_age_commitment = coin->h_age_commitment;
     147          185 :       continue;
     148              :     }
     149          140 :     res->no_age_commitment = no_age_commitment_hash;
     150          140 :     if (! no_denom_pub_hash)
     151          140 :       res->h_denom_pub = h_denom_pub;
     152          140 :     if (! no_age_commitment_hash)
     153           29 :       res->h_age_commitment = h_age_commitment;
     154          140 :     if ( (! no_denom_pub_hash) &&
     155          140 :          (0 != GNUNET_memcmp (&h_denom_pub,
     156              :                               &coin->denom_pub_hash)) )
     157              :     {
     158            6 :       GNUNET_break_op (0);
     159            6 :       res->status = TALER_EXCHANGEDB_CKS_DENOM_CONFLICT;
     160            6 :       continue;
     161              :     }
     162          134 :     if (no_age_commitment_hash != coin->no_age_commitment)
     163              :     {
     164            2 :       GNUNET_break_op (0);
     165            2 :       res->status = no_age_commitment_hash
     166            2 :         ? TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NULL
     167            2 :         : TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NON_NULL;
     168            2 :       continue;
     169              :     }
     170          132 :     if ( (! no_age_commitment_hash) &&
     171           28 :          (0 != GNUNET_memcmp (&h_age_commitment,
     172              :                               &coin->h_age_commitment)) )
     173              :     {
     174            3 :       GNUNET_break_op (0);
     175            3 :       res->status = TALER_EXCHANGEDB_CKS_AGE_CONFLICT_VALUE_DIFFERS;
     176            3 :       continue;
     177              :     }
     178          129 :     res->status = TALER_EXCHANGEDB_CKS_PRESENT;
     179              :   }
     180              : }
     181              : 
     182              : 
     183              : enum GNUNET_DB_QueryStatus
     184          300 : TALER_EXCHANGEDB_do_insert_known_coins (
     185              :   struct TALER_EXCHANGEDB_PostgresContext *pg,
     186              :   unsigned int num_coins,
     187              :   const struct TALER_CoinPublicInfo *const coins[static num_coins],
     188              :   struct TALER_EXCHANGEDB_CoinKnownResult results[static num_coins])
     189          300 : {
     190          300 :   struct TALER_CoinSpendPublicKeyP coin_pubs[num_coins];
     191          300 :   struct TALER_DenominationHashP denom_pub_hashes[num_coins];
     192          300 :   bool has_age_commitments[num_coins];
     193          300 :   struct TALER_AgeCommitmentHashP age_commitment_hashes[num_coins];
     194          300 :   struct TALER_DenominationSignature denom_sigs[num_coins];
     195          300 :   struct GNUNET_PQ_QueryParam params[] = {
     196          300 :     GNUNET_PQ_query_param_array_auto_from_type (num_coins,
     197              :                                                 coin_pubs,
     198              :                                                 pg->conn),
     199          300 :     GNUNET_PQ_query_param_array_auto_from_type (num_coins,
     200              :                                                 denom_pub_hashes,
     201              :                                                 pg->conn),
     202          300 :     GNUNET_PQ_query_param_array_bool (num_coins,
     203              :                                       has_age_commitments,
     204              :                                       pg->conn),
     205          300 :     GNUNET_PQ_query_param_array_auto_from_type (num_coins,
     206              :                                                 age_commitment_hashes,
     207              :                                                 pg->conn),
     208          300 :     TALER_PQ_query_param_array_denom_sig (num_coins,
     209              :                                           denom_sigs,
     210              :                                           pg->conn),
     211              :     GNUNET_PQ_query_param_end
     212              :   };
     213          300 :   struct KnownCoinsContext kcc = {
     214              :     .coins = coins,
     215              :     .results = results,
     216              :     .num_coins = num_coins
     217              :   };
     218              :   enum GNUNET_DB_QueryStatus qs;
     219              : 
     220          300 :   GNUNET_assert (0 < num_coins);
     221              :   /* Gather the columns; the arrays cannot carry NULL elements, so coins
     222              :      without age commitment get a zero hash and a false flag. */
     223          624 :   for (unsigned int i = 0; i < num_coins; i++)
     224              :   {
     225          326 :     const struct TALER_CoinPublicInfo *coin = coins[i];
     226              : 
     227          326 :     coin_pubs[i] = coin->coin_pub;
     228          326 :     denom_pub_hashes[i] = coin->denom_pub_hash;
     229          326 :     has_age_commitments[i] = ! coin->no_age_commitment;
     230          326 :     if (coin->no_age_commitment)
     231          248 :       memset (&age_commitment_hashes[i],
     232              :               0,
     233              :               sizeof (age_commitment_hashes[i]));
     234              :     else
     235           78 :       age_commitment_hashes[i] = coin->h_age_commitment;
     236          326 :     denom_sigs[i] = coin->denom_sig;
     237              :     /* A single INSERT cannot handle the same key twice. */
     238          371 :     for (unsigned int j = 0; j < i; j++)
     239              :     {
     240           47 :       if (0 == GNUNET_memcmp (&coin_pubs[i],
     241              :                               &coin_pubs[j]))
     242              :       {
     243            2 :         GNUNET_break (0);
     244            2 :         GNUNET_PQ_cleanup_query_params_closures (params);
     245            2 :         return GNUNET_DB_STATUS_HARD_ERROR;
     246              :       }
     247              :     }
     248              :   }
     249          298 :   memset (results,
     250              :           0,
     251              :           num_coins * sizeof (*results));
     252          298 :   PREPARE (pg,
     253              :            "do_insert_known_coins",
     254              :            "SELECT"
     255              :            " out_idx"
     256              :            ",out_existed"
     257              :            ",out_known_coin_id"
     258              :            ",out_denom_pub_hash"
     259              :            ",out_age_commitment_hash"
     260              :            " FROM exchange_do_insert_known_coins"
     261              :            " ($1, $2, $3, $4, $5);");
     262          298 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
     263              :                                              "do_insert_known_coins",
     264              :                                              params,
     265              :                                              &known_coins_cb,
     266              :                                              &kcc);
     267          298 :   GNUNET_PQ_cleanup_query_params_closures (params);
     268          298 :   if (0 > qs)
     269            0 :     return qs;
     270          298 :   if ( (kcc.failed) ||
     271          296 :        (kcc.num_rows != num_coins) )
     272              :   {
     273              :     /* an unknown denomination or a malformed reply */
     274            2 :     GNUNET_break (0);
     275            2 :     return GNUNET_DB_STATUS_HARD_ERROR;
     276              :   }
     277          296 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     278              : }
        

Generated by: LCOV version 2.0-1