exchange

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

test_pq.c (19080B)


      1 /*
      2   This file is part of TALER
      3   (C) 2015, 2016, 2023, 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 pq/test_pq.c
     18  * @brief Tests for Postgres convenience API
     19  * @author Christian Grothoff <christian@grothoff.org>
     20  * @author Özgür Kesim <oec-taler@kesim.org>
     21  */
     22 #include "taler/taler_util.h"
     23 #include "taler/taler_pq_lib.h"
     24 #include <gnunet/gnunet_pq_lib.h>
     25 
     26 
     27 /**
     28  * Setup prepared statements.
     29  *
     30  * @param db database handle to initialize
     31  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
     32  */
     33 static enum GNUNET_GenericReturnValue
     34 postgres_prepare (struct GNUNET_PQ_Context *db)
     35 {
     36   struct GNUNET_PQ_PreparedStatement ps[] = {
     37     GNUNET_PQ_make_prepare ("test_insert",
     38                             "INSERT INTO test_pq ("
     39                             "tamount"
     40                             ",json"
     41                             ",aamount"
     42                             ",aamountc"
     43                             ",tamountc"
     44                             ",hash"
     45                             ",hashes"
     46                             ",cs_r_pubs"
     47                             ",denom_sigs"
     48                             ") VALUES "
     49                             "($1, $2, $3, $4, $5, $6, $7, $8, $9);"),
     50     GNUNET_PQ_make_prepare ("test_select_denom_sigs",
     51                             "SELECT"
     52                             " ds.denom_sig"
     53                             " FROM test_pq"
     54                             " CROSS JOIN LATERAL"
     55                             "   UNNEST (denom_sigs) WITH ORDINALITY"
     56                             "     AS ds (denom_sig, idx)"
     57                             " ORDER BY ds.idx;"),
     58     GNUNET_PQ_make_prepare ("test_select",
     59                             "SELECT"
     60                             " tamount"
     61                             ",json"
     62                             ",aamount"
     63                             ",aamountc"
     64                             ",aamountn"
     65                             ",aamountnc"
     66                             ",tamountc"
     67                             ",hash"
     68                             ",hashes"
     69                             ",cs_r_pubs"
     70                             " FROM test_pq;"),
     71     GNUNET_PQ_PREPARED_STATEMENT_END
     72   };
     73 
     74   return GNUNET_PQ_prepare_statements (db,
     75                                        ps);
     76 }
     77 
     78 
     79 /**
     80  * Closure for #denom_sig_cb().
     81  */
     82 struct DenomSigCheck
     83 {
     84   /**
     85    * Signatures we inserted, in order.
     86    */
     87   const struct TALER_DenominationSignature *expected;
     88 
     89   /**
     90    * Number of entries in @e expected.
     91    */
     92   unsigned int num;
     93 
     94   /**
     95    * Rows seen so far.
     96    */
     97   unsigned int seen;
     98 
     99   /**
    100    * Set to true if a row did not match.
    101    */
    102   bool mismatch;
    103 };
    104 
    105 
    106 /**
    107  * Compare each unblinded signature read back from the array
    108  * with the one we inserted at the same position.
    109  *
    110  * @param cls a `struct DenomSigCheck *`
    111  * @param result the result
    112  * @param num_results number of rows
    113  */
    114 static void
    115 denom_sig_cb (void *cls,
    116               PGresult *result,
    117               unsigned int num_results)
    118 {
    119   struct DenomSigCheck *dsc = cls;
    120 
    121   for (unsigned int i = 0; i < num_results; i++)
    122   {
    123     struct TALER_DenominationSignature got = { 0 };
    124     struct GNUNET_PQ_ResultSpec rs[] = {
    125       GNUNET_PQ_result_spec_unblinded_sig ("denom_sig",
    126                                            &got.unblinded_sig),
    127       GNUNET_PQ_result_spec_end
    128     };
    129 
    130     if (GNUNET_OK !=
    131         GNUNET_PQ_extract_result (result,
    132                                   rs,
    133                                   i))
    134     {
    135       GNUNET_break (0);
    136       dsc->mismatch = true;
    137       return;
    138     }
    139     if ( (dsc->seen >= dsc->num) ||
    140          (0 != TALER_denom_sig_cmp (&got,
    141                                     &dsc->expected[dsc->seen])) )
    142     {
    143       GNUNET_break (0);
    144       dsc->mismatch = true;
    145     }
    146     dsc->seen++;
    147     GNUNET_PQ_cleanup_result (rs);
    148   }
    149 }
    150 
    151 
    152 /**
    153  * Run actual test queries.
    154  *
    155  * @return 0 on success
    156  */
    157 static int
    158 run_queries (struct GNUNET_PQ_Context *conn)
    159 {
    160   struct TALER_DenominationSignature denom_sigs[2];
    161   struct TALER_Amount tamount;
    162   struct TALER_Amount aamount[3];
    163   struct TALER_Amount aamountc[2];
    164   struct TALER_Amount tamountc;
    165   struct GNUNET_HashCode hc =
    166   {{0xdeadbeef,0xdeadbeef,0xdeadbeef,0xdeadbeef,
    167     0xdeadbeef,0xdeadbeef,0xdeadbeef,0xdeadbeef,
    168     0xdeadbeef,0xdeadbeef,0xdeadbeef,0xdeadbeef,
    169     0xdeadbeef,0xdeadbeef,0xdeadbeef,0xdeadbeef, }};
    170   struct GNUNET_HashCode hcs[2] =
    171   {{{0xc0feec0f,0xc0feec0f,0xc0feec0f,0xc0feec0f,
    172      0xc0feec0f,0xc0feec0f,0xc0feec0f,0xc0feec0f,
    173      0xc0feec0f,0xc0feec0f,0xc0feec0f,0xc0feec0f,
    174      0xc0feec0f,0xc0feec0f,0xc0feec0f,0xc0feec0f,}},
    175    {{0xdeadbeaf,0xdeadbeaf,0xdeadbeaf,0xdeadbeaf,
    176      0xdeadbeaf,0xdeadbeaf,0xdeadbeaf,0xdeadbeaf,
    177      0xdeadbeaf,0xdeadbeaf,0xdeadbeaf,0xdeadbeaf,
    178      0xdeadbeaf,0xdeadbeaf,0xdeadbeaf,0xdeadbeaf,}}};
    179   struct GNUNET_CRYPTO_CSPublicRPairP in_cs_r_pubs[5];
    180   json_t *json;
    181 
    182   GNUNET_assert (GNUNET_OK ==
    183                  TALER_string_to_amount ("EUR:5.3",
    184                                          &aamount[0]));
    185   GNUNET_assert (GNUNET_OK ==
    186                  TALER_string_to_amount ("EUR:6.4",
    187                                          &aamount[1]));
    188   GNUNET_assert (GNUNET_OK ==
    189                  TALER_string_to_amount ("EUR:7.5",
    190                                          &aamount[2]));
    191   GNUNET_assert (GNUNET_OK ==
    192                  TALER_string_to_amount ("EUR:7.7",
    193                                          &tamount));
    194   GNUNET_assert (GNUNET_OK ==
    195                  TALER_string_to_amount ("USD:3.2",
    196                                          &aamountc[0]));
    197   GNUNET_assert (GNUNET_OK ==
    198                  TALER_string_to_amount ("CHF:4.5",
    199                                          &aamountc[1]));
    200   GNUNET_assert (GNUNET_OK ==
    201                  TALER_string_to_amount ("FOO:8.7",
    202                                          &tamountc));
    203   json = json_object ();
    204   GNUNET_assert (NULL != json);
    205   GNUNET_assert (0 ==
    206                  json_object_set_new (json,
    207                                       "foo",
    208                                       json_integer (42)));
    209   GNUNET_CRYPTO_random_block (in_cs_r_pubs,
    210                               sizeof(struct GNUNET_CRYPTO_CSPublicRPairP) * 5);
    211   /* one CS and one RSA signature: the two encodings differ in size */
    212   {
    213     struct GNUNET_CRYPTO_UnblindedSignature *ubs;
    214     struct GNUNET_CRYPTO_RsaPrivateKey *rsa_priv;
    215 
    216     ubs = GNUNET_new (struct GNUNET_CRYPTO_UnblindedSignature);
    217     ubs->cipher = GNUNET_CRYPTO_BSA_CS;
    218     ubs->rc = 1;
    219     GNUNET_CRYPTO_random_block (&ubs->details.cs_signature,
    220                                 sizeof (ubs->details.cs_signature));
    221     denom_sigs[0].unblinded_sig = ubs;
    222 
    223     rsa_priv = GNUNET_CRYPTO_rsa_private_key_create (1024);
    224     GNUNET_assert (NULL != rsa_priv);
    225     ubs = GNUNET_new (struct GNUNET_CRYPTO_UnblindedSignature);
    226     ubs->cipher = GNUNET_CRYPTO_BSA_RSA;
    227     ubs->rc = 1;
    228     ubs->details.rsa_signature
    229       = GNUNET_CRYPTO_rsa_sign_fdh (rsa_priv,
    230                                     &hc,
    231                                     sizeof (hc));
    232     GNUNET_assert (NULL != ubs->details.rsa_signature);
    233     GNUNET_CRYPTO_rsa_private_key_free (rsa_priv);
    234     denom_sigs[1].unblinded_sig = ubs;
    235   }
    236   {
    237     struct GNUNET_PQ_QueryParam params_insert[] = {
    238       TALER_PQ_query_param_amount (conn,
    239                                    &tamount),
    240       TALER_PQ_query_param_json (json),
    241       TALER_PQ_query_param_array_amount (3,
    242                                          aamount,
    243                                          conn),
    244       TALER_PQ_query_param_array_amount_with_currency (2,
    245                                                        aamountc,
    246                                                        "merchant",
    247                                                        conn),
    248       TALER_PQ_query_param_amount_with_currency (conn,
    249                                                  &tamountc),
    250       GNUNET_PQ_query_param_fixed_size (&hc,
    251                                         sizeof (hc)),
    252       TALER_PQ_query_param_array_hash_code (2,
    253                                             hcs,
    254                                             conn),
    255       TALER_PQ_query_param_array_cs_r_pub (5,
    256                                            in_cs_r_pubs,
    257                                            conn),
    258       TALER_PQ_query_param_array_denom_sig (2,
    259                                             denom_sigs,
    260                                             conn),
    261       GNUNET_PQ_query_param_end
    262     };
    263     PGresult *result;
    264 
    265     result = GNUNET_PQ_exec_prepared (conn,
    266                                       "test_insert",
    267                                       params_insert);
    268     for (uint8_t i = 0; i < 5; i++)
    269     {
    270       printf (" in_cs_r_pubs[%d]=%s\n",
    271               i,
    272               GNUNET_STRINGS_data_to_string_alloc (
    273                 &in_cs_r_pubs[i],
    274                 sizeof(in_cs_r_pubs[i])));
    275     }
    276 
    277     if (PGRES_COMMAND_OK != PQresultStatus (result))
    278     {
    279       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    280                   "Database failure: %s\n",
    281                   PQresultErrorMessage (result));
    282       PQclear (result);
    283       return 1;
    284     }
    285     PQclear (result);
    286     json_decref (json);
    287   }
    288   {
    289     struct TALER_Amount tamount2;
    290     struct TALER_Amount tamountc2;
    291     struct TALER_Amount *pamount;
    292     struct TALER_Amount *pamountc;
    293     struct TALER_Amount *pamountn;
    294     struct TALER_Amount *pamountnc;
    295     struct GNUNET_HashCode hc2;
    296     struct GNUNET_HashCode *hcs2;
    297     struct GNUNET_CRYPTO_CSPublicRPairP *out_cs_r_pubs;
    298     size_t npamount;
    299     size_t npamountc;
    300     size_t npamountn;
    301     size_t npamountnc;
    302     size_t nhcs;
    303     size_t n_rpubs;
    304     json_t *json2;
    305     struct GNUNET_PQ_QueryParam params_select[] = {
    306       GNUNET_PQ_query_param_end
    307     };
    308     struct GNUNET_PQ_ResultSpec results_select[] = {
    309       TALER_PQ_result_spec_amount ("tamount",
    310                                    "EUR",
    311                                    &tamount2),
    312       TALER_PQ_result_spec_json ("json",
    313                                  &json2),
    314       TALER_PQ_result_spec_array_amount (conn,
    315                                          "aamount",
    316                                          "EUR",
    317                                          &npamount,
    318                                          &pamount),
    319       TALER_PQ_result_spec_array_amount_with_currency (conn,
    320                                                        "merchant",
    321                                                        "aamountc",
    322                                                        &npamountc,
    323                                                        &pamountc),
    324       TALER_PQ_result_spec_array_amount (conn,
    325                                          "aamountn",
    326                                          "EUR",
    327                                          &npamountn,
    328                                          &pamountn),
    329       TALER_PQ_result_spec_array_amount_with_currency (conn,
    330                                                        "merchant",
    331                                                        "aamountnc",
    332                                                        &npamountnc,
    333                                                        &pamountnc),
    334       TALER_PQ_result_spec_amount_with_currency ("tamountc",
    335                                                  &tamountc2),
    336       GNUNET_PQ_result_spec_auto_from_type ("hash",
    337                                             &hc2),
    338       TALER_PQ_result_spec_array_hash_code (conn,
    339                                             "hashes",
    340                                             &nhcs,
    341                                             &hcs2),
    342       TALER_PQ_result_spec_array_cs_r_pub (conn,
    343                                            "cs_r_pubs",
    344                                            &n_rpubs,
    345                                            &out_cs_r_pubs),
    346       GNUNET_PQ_result_spec_end
    347     };
    348 
    349     if (1 !=
    350         GNUNET_PQ_eval_prepared_singleton_select (conn,
    351                                                   "test_select",
    352                                                   params_select,
    353                                                   results_select))
    354     {
    355       GNUNET_break (0);
    356       return 1;
    357     }
    358     GNUNET_break (0 ==
    359                   TALER_amount_cmp (&tamount,
    360                                     &tamount2));
    361     GNUNET_break (42 ==
    362                   json_integer_value (json_object_get (json2,
    363                                                        "foo")));
    364     GNUNET_break (3 == npamount);
    365     for (size_t i = 0; i < 3; i++)
    366     {
    367       GNUNET_break (0 ==
    368                     TALER_amount_cmp (&aamount[i],
    369                                       &pamount[i]));
    370     }
    371     GNUNET_break (2 == npamountc);
    372     for (size_t i = 0; i < npamountc; i++)
    373     {
    374       GNUNET_break (0 ==
    375                     TALER_amount_cmp (&aamountc[i],
    376                                       &pamountc[i]));
    377     }
    378     GNUNET_break (0 ==
    379                   TALER_amount_cmp (&tamountc,
    380                                     &tamountc2));
    381     GNUNET_break (0 == GNUNET_memcmp (&hc,&hc2));
    382     for (size_t i = 0; i < 2; i++)
    383     {
    384       GNUNET_break (0 ==
    385                     GNUNET_memcmp (&hcs[i],
    386                                    &hcs2[i]));
    387     }
    388     GNUNET_break (5 == n_rpubs);
    389     for (uint8_t i = 0; i < 5; i++)
    390     {
    391       GNUNET_break (0 ==
    392                     GNUNET_memcmp (&in_cs_r_pubs[i],
    393                                    &out_cs_r_pubs[i]));
    394       printf ("out_cs_r_pubs[%d]=%s\n",
    395               i,
    396               GNUNET_STRINGS_data_to_string_alloc (
    397                 &out_cs_r_pubs[i],
    398                 sizeof(out_cs_r_pubs[i])));
    399     }
    400     GNUNET_PQ_cleanup_result (results_select);
    401   }
    402   {
    403     struct DenomSigCheck dsc = {
    404       .expected = denom_sigs,
    405       .num = 2
    406     };
    407     struct GNUNET_PQ_QueryParam params_select[] = {
    408       GNUNET_PQ_query_param_end
    409     };
    410 
    411     if (2 !=
    412         GNUNET_PQ_eval_prepared_multi_select (conn,
    413                                               "test_select_denom_sigs",
    414                                               params_select,
    415                                               &denom_sig_cb,
    416                                               &dsc))
    417     {
    418       GNUNET_break (0);
    419       return 1;
    420     }
    421     GNUNET_break (2 == dsc.seen);
    422     GNUNET_break (! dsc.mismatch);
    423     if ( (2 != dsc.seen) ||
    424          (dsc.mismatch) )
    425       return 1;
    426   }
    427   TALER_denom_sig_free (&denom_sigs[0]);
    428   TALER_denom_sig_free (&denom_sigs[1]);
    429   return 0;
    430 }
    431 
    432 
    433 int
    434 main (int argc,
    435       const char *const argv[])
    436 {
    437   struct GNUNET_PQ_ExecuteStatement es[] = {
    438     GNUNET_PQ_make_execute ("DO $$ "
    439                             " BEGIN"
    440                             " CREATE SCHEMA IF NOT EXISTS merchant;"
    441                             " END "
    442                             "$$;"),
    443     GNUNET_PQ_make_execute ("DO $$ "
    444                             " BEGIN"
    445                             " CREATE DOMAIN gnunet_hashcode AS BYTEA"
    446                             "   CHECK(length(VALUE)=64);"
    447                             " EXCEPTION"
    448                             "   WHEN duplicate_object THEN null;"
    449                             " END "
    450                             "$$;"),
    451     GNUNET_PQ_make_execute ("DO $$ "
    452                             " BEGIN"
    453                             " CREATE TYPE taler_amount AS"
    454                             "   (val INT8, frac INT4);"
    455                             " EXCEPTION"
    456                             "   WHEN duplicate_object THEN null;"
    457                             " END "
    458                             "$$;"),
    459     GNUNET_PQ_make_execute ("DO $$ "
    460                             " BEGIN"
    461                             " CREATE TYPE merchant.taler_amount_currency AS"
    462                             "   (val INT8, frac INT4, curr VARCHAR(12));"
    463                             " EXCEPTION"
    464                             "   WHEN duplicate_object THEN null;"
    465                             " END "
    466                             "$$;"),
    467     GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS test_pq ("
    468                             "tamount taler_amount NOT NULL"
    469                             ",json VARCHAR NOT NULL"
    470                             ",aamount taler_amount[]"
    471                             ",aamountc merchant.taler_amount_currency[]"
    472                             ",aamountn taler_amount[] NOT NULL DEFAULT ARRAY[]::taler_amount[]"
    473                             ",aamountnc merchant.taler_amount_currency[] NOT NULL DEFAULT ARRAY[]::merchant.taler_amount_currency[]"
    474                             ",tamountc merchant.taler_amount_currency"
    475                             ",hash gnunet_hashcode"
    476                             ",hashes gnunet_hashcode[]"
    477                             ",cs_r_pubs BYTEA[]"
    478                             ",denom_sigs BYTEA[]"
    479                             ")"),
    480     GNUNET_PQ_EXECUTE_STATEMENT_END
    481   };
    482   struct GNUNET_PQ_Context *conn;
    483   int ret;
    484   struct GNUNET_CONFIGURATION_Handle *cfg;
    485 
    486   (void) argc;
    487   (void) argv;
    488   GNUNET_log_setup ("test-pq",
    489                     "WARNING",
    490                     NULL);
    491   cfg = GNUNET_CONFIGURATION_create (
    492     TALER_EXCHANGE_project_data ());
    493   GNUNET_CONFIGURATION_set_value_string (cfg,
    494                                          "test-pq",
    495                                          "CONFIG",
    496                                          "postgres:///talercheck");
    497   /* just to squash warning */
    498   GNUNET_CONFIGURATION_set_value_string (cfg,
    499                                          "test-pq",
    500                                          "SQL_DIR",
    501                                          "none");
    502   conn = GNUNET_PQ_init (cfg,
    503                          "test-pq",
    504                          NULL,
    505                          NULL);
    506   GNUNET_CONFIGURATION_destroy (cfg);
    507   if (NULL == conn)
    508     return 77;
    509   GNUNET_assert (GNUNET_OK ==
    510                  GNUNET_PQ_exec_statements (conn,
    511                                             es));
    512   if (GNUNET_OK !=
    513       postgres_prepare (conn))
    514   {
    515     GNUNET_break (0);
    516     GNUNET_PQ_disconnect (conn);
    517     return 1;
    518   }
    519   ret = run_queries (conn);
    520   {
    521     struct GNUNET_PQ_ExecuteStatement ds[] = {
    522       GNUNET_PQ_make_execute ("DROP TABLE test_pq"),
    523       GNUNET_PQ_EXECUTE_STATEMENT_END
    524     };
    525 
    526     if (GNUNET_OK !=
    527         GNUNET_PQ_exec_statements (conn,
    528                                    ds))
    529     {
    530       fprintf (stderr,
    531                "Failed to drop table\n");
    532       GNUNET_PQ_disconnect (conn);
    533       return 1;
    534     }
    535   }
    536   GNUNET_PQ_disconnect (conn);
    537   return ret;
    538 }
    539 
    540 
    541 /* end of test_pq.c */