exchange

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

pg_batch_ensure_coin_known.c (16415B)


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