exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

batch_ensure_coin_known.c (16369B)


      1 /*
      2    This file is part of TALER
      3    Copyright (C) 2022 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/batch_ensure_coin_known.c
     18  * @brief Implementation of the batch_ensure_coin_known function for Postgres
     19  * @author Christian Grothoff
     20  *
     21  * FIXME-#9373: use the array support for postgres to simplify this code!
     22  *
     23  */
     24 #include "exchangedb_lib.h"
     25 #include "taler/taler_pq_lib.h"
     26 #include "exchange-database/batch_ensure_coin_known.h"
     27 #include "helper.h"
     28 
     29 
     30 static enum GNUNET_DB_QueryStatus
     31 insert1 (struct TALER_EXCHANGEDB_PostgresContext *pg,
     32          const struct TALER_CoinPublicInfo coin[1],
     33          struct TALER_EXCHANGEDB_CoinInfo result[1])
     34 {
     35   enum GNUNET_DB_QueryStatus qs;
     36   bool is_denom_pub_hash_null = false;
     37   bool is_age_hash_null = false;
     38   struct GNUNET_PQ_QueryParam params[] = {
     39     GNUNET_PQ_query_param_auto_from_type (&coin[0].coin_pub),
     40     GNUNET_PQ_query_param_auto_from_type (&coin[0].denom_pub_hash),
     41     GNUNET_PQ_query_param_auto_from_type (&coin[0].h_age_commitment),
     42     TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
     43     GNUNET_PQ_query_param_end
     44   };
     45   struct GNUNET_PQ_ResultSpec rs[] = {
     46     GNUNET_PQ_result_spec_bool ("existed",
     47                                 &result[0].existed),
     48     GNUNET_PQ_result_spec_uint64 ("known_coin_id",
     49                                   &result[0].known_coin_id),
     50     GNUNET_PQ_result_spec_allow_null (
     51       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
     52                                             &result[0].denom_hash),
     53       &is_denom_pub_hash_null),
     54     GNUNET_PQ_result_spec_allow_null (
     55       GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
     56                                             &result[0].h_age_commitment),
     57       &is_age_hash_null),
     58     GNUNET_PQ_result_spec_end
     59   };
     60 
     61   PREPARE (pg,
     62            "batch1_known_coin",
     63            "SELECT"
     64            " existed1 AS existed"
     65            ",known_coin_id1 AS known_coin_id"
     66            ",denom_pub_hash1 AS denom_hash"
     67            ",age_commitment_hash1 AS h_age_commitment"
     68            " FROM exchange_do_batch1_known_coin"
     69            "  ($1, $2, $3, $4);"
     70            );
     71   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
     72                                                  "batch1_known_coin",
     73                                                  params,
     74                                                  rs);
     75   switch (qs)
     76   {
     77   case GNUNET_DB_STATUS_HARD_ERROR:
     78     GNUNET_break (0);
     79     return qs;
     80   case GNUNET_DB_STATUS_SOFT_ERROR:
     81     return qs;
     82   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     83     GNUNET_break (0); /* should be impossible */
     84     return GNUNET_DB_STATUS_HARD_ERROR;
     85   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     86     break; /* continued below */
     87   }
     88 
     89   if ( (! is_denom_pub_hash_null) &&
     90        (0 != GNUNET_memcmp (&result[0].denom_hash,
     91                             &coin->denom_pub_hash)) )
     92   {
     93     GNUNET_break_op (0);
     94     result[0].denom_conflict = true;
     95   }
     96 
     97   if ( (! is_denom_pub_hash_null) &&
     98        (0 != GNUNET_memcmp (&result[0].denom_hash,
     99                             &coin[0].denom_pub_hash)) )
    100   {
    101     GNUNET_break_op (0);
    102     result[0].denom_conflict = true;
    103   }
    104 
    105   result[0].age_conflict = TALER_AgeCommitmentHashP_NoConflict;
    106 
    107   if (is_age_hash_null != coin[0].no_age_commitment)
    108   {
    109     if (is_age_hash_null)
    110     {
    111       GNUNET_break_op (0);
    112       result[0].age_conflict = TALER_AgeCommitmentHashP_NullExpected;
    113     }
    114     else
    115     {
    116       GNUNET_break_op (0);
    117       result[0].age_conflict = TALER_AgeCommitmentHashP_ValueExpected;
    118     }
    119   }
    120   else if ( (! is_age_hash_null) &&
    121             (0 != GNUNET_memcmp (&result[0].h_age_commitment,
    122                                  &coin[0].h_age_commitment)) )
    123   {
    124     GNUNET_break_op (0);
    125     result[0].age_conflict = TALER_AgeCommitmentHashP_ValueDiffers;
    126   }
    127 
    128   return qs;
    129 }
    130 
    131 
    132 static enum GNUNET_DB_QueryStatus
    133 insert2 (struct TALER_EXCHANGEDB_PostgresContext *pg,
    134          const struct TALER_CoinPublicInfo coin[2],
    135          struct TALER_EXCHANGEDB_CoinInfo result[2])
    136 {
    137   enum GNUNET_DB_QueryStatus qs;
    138   bool is_denom_pub_hash_null[2] = {false, false};
    139   bool is_age_hash_null[2] = {false, false};
    140   struct GNUNET_PQ_QueryParam params[] = {
    141     GNUNET_PQ_query_param_auto_from_type (&coin[0].coin_pub),
    142     GNUNET_PQ_query_param_auto_from_type (&coin[0].denom_pub_hash),
    143     GNUNET_PQ_query_param_auto_from_type (&coin[0].h_age_commitment),
    144     TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
    145 
    146     GNUNET_PQ_query_param_auto_from_type (&coin[1].coin_pub),
    147     GNUNET_PQ_query_param_auto_from_type (&coin[1].denom_pub_hash),
    148     GNUNET_PQ_query_param_auto_from_type (&coin[1].h_age_commitment),
    149     TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
    150     GNUNET_PQ_query_param_end
    151   };
    152   struct GNUNET_PQ_ResultSpec rs[] = {
    153     GNUNET_PQ_result_spec_bool ("existed",
    154                                 &result[0].existed),
    155     GNUNET_PQ_result_spec_uint64 ("known_coin_id",
    156                                   &result[0].known_coin_id),
    157     GNUNET_PQ_result_spec_allow_null (
    158       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    159                                             &result[0].denom_hash),
    160       &is_denom_pub_hash_null[0]),
    161     GNUNET_PQ_result_spec_allow_null (
    162       GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
    163                                             &result[0].h_age_commitment),
    164       &is_age_hash_null[0]),
    165     GNUNET_PQ_result_spec_bool ("existed2",
    166                                 &result[1].existed),
    167     GNUNET_PQ_result_spec_uint64 ("known_coin_id2",
    168                                   &result[1].known_coin_id),
    169     GNUNET_PQ_result_spec_allow_null (
    170       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash2",
    171                                             &result[1].denom_hash),
    172       &is_denom_pub_hash_null[1]),
    173     GNUNET_PQ_result_spec_allow_null (
    174       GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash2",
    175                                             &result[1].h_age_commitment),
    176       &is_age_hash_null[1]),
    177     GNUNET_PQ_result_spec_end
    178   };
    179 
    180   PREPARE (pg,
    181            "batch2_known_coin",
    182            "SELECT"
    183            " existed1 AS existed"
    184            ",known_coin_id1 AS known_coin_id"
    185            ",denom_pub_hash1 AS denom_hash"
    186            ",age_commitment_hash1 AS h_age_commitment"
    187            ",existed2 AS existed2"
    188            ",known_coin_id2 AS known_coin_id2"
    189            ",denom_pub_hash2 AS denom_hash2"
    190            ",age_commitment_hash2 AS h_age_commitment2"
    191            " FROM exchange_do_batch2_known_coin"
    192            "  ($1, $2, $3, $4, $5, $6, $7, $8);"
    193            );
    194   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    195                                                  "batch2_known_coin",
    196                                                  params,
    197                                                  rs);
    198   switch (qs)
    199   {
    200   case GNUNET_DB_STATUS_HARD_ERROR:
    201     GNUNET_break (0);
    202     return qs;
    203   case GNUNET_DB_STATUS_SOFT_ERROR:
    204     return qs;
    205   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    206     GNUNET_break (0); /* should be impossible */
    207     return GNUNET_DB_STATUS_HARD_ERROR;
    208   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    209     break; /* continued below */
    210   }
    211 
    212   for (int i = 0; i < 2; i++)
    213   {
    214     if ( (! is_denom_pub_hash_null[i]) &&
    215          (0 != GNUNET_memcmp (&result[i].denom_hash,
    216                               &coin[i].denom_pub_hash)) )
    217     {
    218       GNUNET_break_op (0);
    219       result[i].denom_conflict = true;
    220     }
    221 
    222     result[i].age_conflict = TALER_AgeCommitmentHashP_NoConflict;
    223 
    224     if (is_age_hash_null[i] != coin[i].no_age_commitment)
    225     {
    226       if (is_age_hash_null[i])
    227       {
    228         GNUNET_break_op (0);
    229         result[i].age_conflict = TALER_AgeCommitmentHashP_NullExpected;
    230       }
    231       else
    232       {
    233         GNUNET_break_op (0);
    234         result[i].age_conflict = TALER_AgeCommitmentHashP_ValueExpected;
    235       }
    236     }
    237     else if ( (! is_age_hash_null[i]) &&
    238               (0 != GNUNET_memcmp (&result[i].h_age_commitment,
    239                                    &coin[i].h_age_commitment)) )
    240     {
    241       GNUNET_break_op (0);
    242       result[i].age_conflict = TALER_AgeCommitmentHashP_ValueDiffers;
    243     }
    244   }
    245 
    246   return qs;
    247 }
    248 
    249 
    250 static enum GNUNET_DB_QueryStatus
    251 insert4 (struct TALER_EXCHANGEDB_PostgresContext *pg,
    252          const struct TALER_CoinPublicInfo coin[4],
    253          struct TALER_EXCHANGEDB_CoinInfo result[4])
    254 {
    255   enum GNUNET_DB_QueryStatus qs;
    256   bool is_denom_pub_hash_null[4] = {false, false, false, false};
    257   bool is_age_hash_null[4] = {false, false, false, false};
    258   struct GNUNET_PQ_QueryParam params[] = {
    259     GNUNET_PQ_query_param_auto_from_type (&coin[0].coin_pub),
    260     GNUNET_PQ_query_param_auto_from_type (&coin[0].denom_pub_hash),
    261     GNUNET_PQ_query_param_auto_from_type (&coin[0].h_age_commitment),
    262     TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
    263 
    264     GNUNET_PQ_query_param_auto_from_type (&coin[1].coin_pub),
    265     GNUNET_PQ_query_param_auto_from_type (&coin[1].denom_pub_hash),
    266     GNUNET_PQ_query_param_auto_from_type (&coin[1].h_age_commitment),
    267     TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
    268 
    269     GNUNET_PQ_query_param_auto_from_type (&coin[2].coin_pub),
    270     GNUNET_PQ_query_param_auto_from_type (&coin[2].denom_pub_hash),
    271     GNUNET_PQ_query_param_auto_from_type (&coin[2].h_age_commitment),
    272     TALER_PQ_query_param_denom_sig (&coin[2].denom_sig),
    273 
    274     GNUNET_PQ_query_param_auto_from_type (&coin[3].coin_pub),
    275     GNUNET_PQ_query_param_auto_from_type (&coin[3].denom_pub_hash),
    276     GNUNET_PQ_query_param_auto_from_type (&coin[3].h_age_commitment),
    277     TALER_PQ_query_param_denom_sig (&coin[3].denom_sig),
    278     GNUNET_PQ_query_param_end
    279   };
    280   struct GNUNET_PQ_ResultSpec rs[] = {
    281     GNUNET_PQ_result_spec_bool ("existed",
    282                                 &result[0].existed),
    283     GNUNET_PQ_result_spec_uint64 ("known_coin_id",
    284                                   &result[0].known_coin_id),
    285     GNUNET_PQ_result_spec_allow_null (
    286       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    287                                             &result[0].denom_hash),
    288       &is_denom_pub_hash_null[0]),
    289     GNUNET_PQ_result_spec_allow_null (
    290       GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
    291                                             &result[0].h_age_commitment),
    292       &is_age_hash_null[0]),
    293     GNUNET_PQ_result_spec_bool ("existed2",
    294                                 &result[1].existed),
    295     GNUNET_PQ_result_spec_uint64 ("known_coin_id2",
    296                                   &result[1].known_coin_id),
    297     GNUNET_PQ_result_spec_allow_null (
    298       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash2",
    299                                             &result[1].denom_hash),
    300       &is_denom_pub_hash_null[1]),
    301     GNUNET_PQ_result_spec_allow_null (
    302       GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash2",
    303                                             &result[1].h_age_commitment),
    304       &is_age_hash_null[1]),
    305     GNUNET_PQ_result_spec_bool ("existed3",
    306                                 &result[2].existed),
    307     GNUNET_PQ_result_spec_uint64 ("known_coin_id3",
    308                                   &result[2].known_coin_id),
    309     GNUNET_PQ_result_spec_allow_null (
    310       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash3",
    311                                             &result[2].denom_hash),
    312       &is_denom_pub_hash_null[2]),
    313     GNUNET_PQ_result_spec_allow_null (
    314       GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash3",
    315                                             &result[2].h_age_commitment),
    316       &is_age_hash_null[2]),
    317     GNUNET_PQ_result_spec_bool ("existed4",
    318                                 &result[3].existed),
    319     GNUNET_PQ_result_spec_uint64 ("known_coin_id4",
    320                                   &result[3].known_coin_id),
    321     GNUNET_PQ_result_spec_allow_null (
    322       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash4",
    323                                             &result[3].denom_hash),
    324       &is_denom_pub_hash_null[3]),
    325     GNUNET_PQ_result_spec_allow_null (
    326       GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash4",
    327                                             &result[3].h_age_commitment),
    328       &is_age_hash_null[3]),
    329     GNUNET_PQ_result_spec_end
    330   };
    331 
    332   PREPARE (pg,
    333            "batch4_known_coin",
    334            "SELECT"
    335            " existed1 AS existed"
    336            ",known_coin_id1 AS known_coin_id"
    337            ",denom_pub_hash1 AS denom_hash"
    338            ",age_commitment_hash1 AS h_age_commitment"
    339            ",existed2 AS existed2"
    340            ",known_coin_id2 AS known_coin_id2"
    341            ",denom_pub_hash2 AS denom_hash2"
    342            ",age_commitment_hash2 AS h_age_commitment2"
    343            ",existed3 AS existed3"
    344            ",known_coin_id3 AS known_coin_id3"
    345            ",denom_pub_hash3 AS denom_hash3"
    346            ",age_commitment_hash3 AS h_age_commitment3"
    347            ",existed4 AS existed4"
    348            ",known_coin_id4 AS known_coin_id4"
    349            ",denom_pub_hash4 AS denom_hash4"
    350            ",age_commitment_hash4 AS h_age_commitment4"
    351            " FROM exchange_do_batch2_known_coin"
    352            "  ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16);"
    353            );
    354   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    355                                                  "batch4_known_coin",
    356                                                  params,
    357                                                  rs);
    358   switch (qs)
    359   {
    360   case GNUNET_DB_STATUS_HARD_ERROR:
    361     GNUNET_break (0);
    362     return qs;
    363   case GNUNET_DB_STATUS_SOFT_ERROR:
    364     return qs;
    365   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    366     GNUNET_break (0); /* should be impossible */
    367     return GNUNET_DB_STATUS_HARD_ERROR;
    368   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    369     break; /* continued below */
    370   }
    371 
    372   for (int i = 0; i < 4; i++)
    373   {
    374     if ( (! is_denom_pub_hash_null[i]) &&
    375          (0 != GNUNET_memcmp (&result[i].denom_hash,
    376                               &coin[i].denom_pub_hash)) )
    377     {
    378       GNUNET_break_op (0);
    379       result[i].denom_conflict = true;
    380     }
    381 
    382     result[i].age_conflict = TALER_AgeCommitmentHashP_NoConflict;
    383 
    384     if (is_age_hash_null[i] != coin[i].no_age_commitment)
    385     {
    386       if (is_age_hash_null[i])
    387       {
    388         GNUNET_break_op (0);
    389         result[i].age_conflict = TALER_AgeCommitmentHashP_NullExpected;
    390       }
    391       else
    392       {
    393         GNUNET_break_op (0);
    394         result[i].age_conflict = TALER_AgeCommitmentHashP_ValueExpected;
    395       }
    396     }
    397     else if ( (! is_age_hash_null[i]) &&
    398               (0 != GNUNET_memcmp (&result[i].h_age_commitment,
    399                                    &coin[i].h_age_commitment)) )
    400     {
    401       GNUNET_break_op (0);
    402       result[i].age_conflict = TALER_AgeCommitmentHashP_ValueDiffers;
    403     }
    404   }
    405 
    406   return qs;
    407 }
    408 
    409 
    410 enum GNUNET_DB_QueryStatus
    411 TALER_EXCHANGEDB_batch_ensure_coin_known (
    412   struct TALER_EXCHANGEDB_PostgresContext *pg,
    413   const struct TALER_CoinPublicInfo *coin,
    414   struct TALER_EXCHANGEDB_CoinInfo *result,
    415   unsigned int coin_length,
    416   unsigned int batch_size)
    417 {
    418   enum GNUNET_DB_QueryStatus qs = 0;
    419   unsigned int i = 0;
    420 
    421   while ( (qs >= 0) &&
    422           (i < coin_length) )
    423   {
    424     unsigned int bs = GNUNET_MIN (batch_size,
    425                                   coin_length - i);
    426     if (bs >= 4)
    427     {
    428       qs = insert4 (pg,
    429                     &coin[i],
    430                     &result[i]);
    431       i += 4;
    432       continue;
    433     }
    434     switch (bs)
    435     {
    436     case 3:
    437     case 2:
    438       qs = insert2 (pg,
    439                     &coin[i],
    440                     &result[i]);
    441       i += 2;
    442       break;
    443     case 1:
    444       qs = insert1 (pg,
    445                     &coin[i],
    446                     &result[i]);
    447       i += 1;
    448       break;
    449     case 0:
    450       GNUNET_assert (0);
    451       break;
    452     }
    453   } /* end while */
    454   if (qs < 0)
    455     return qs;
    456   return i;
    457 }