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 */