Line data Source code
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 1 : postgres_prepare (struct GNUNET_PQ_Context *db)
35 : {
36 1 : struct GNUNET_PQ_PreparedStatement ps[] = {
37 1 : 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 1 : 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 1 : 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 1 : 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 1 : denom_sig_cb (void *cls,
116 : PGresult *result,
117 : unsigned int num_results)
118 : {
119 1 : struct DenomSigCheck *dsc = cls;
120 :
121 3 : for (unsigned int i = 0; i < num_results; i++)
122 : {
123 2 : struct TALER_DenominationSignature got = { 0 };
124 2 : struct GNUNET_PQ_ResultSpec rs[] = {
125 2 : GNUNET_PQ_result_spec_unblinded_sig ("denom_sig",
126 : &got.unblinded_sig),
127 : GNUNET_PQ_result_spec_end
128 : };
129 :
130 2 : if (GNUNET_OK !=
131 2 : GNUNET_PQ_extract_result (result,
132 : rs,
133 : i))
134 : {
135 0 : GNUNET_break (0);
136 0 : dsc->mismatch = true;
137 0 : return;
138 : }
139 4 : if ( (dsc->seen >= dsc->num) ||
140 2 : (0 != TALER_denom_sig_cmp (&got,
141 2 : &dsc->expected[dsc->seen])) )
142 : {
143 0 : GNUNET_break (0);
144 0 : dsc->mismatch = true;
145 : }
146 2 : dsc->seen++;
147 2 : 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 1 : 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 1 : 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 1 : 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 1 : GNUNET_assert (GNUNET_OK ==
183 : TALER_string_to_amount ("EUR:5.3",
184 : &aamount[0]));
185 1 : GNUNET_assert (GNUNET_OK ==
186 : TALER_string_to_amount ("EUR:6.4",
187 : &aamount[1]));
188 1 : GNUNET_assert (GNUNET_OK ==
189 : TALER_string_to_amount ("EUR:7.5",
190 : &aamount[2]));
191 1 : GNUNET_assert (GNUNET_OK ==
192 : TALER_string_to_amount ("EUR:7.7",
193 : &tamount));
194 1 : GNUNET_assert (GNUNET_OK ==
195 : TALER_string_to_amount ("USD:3.2",
196 : &aamountc[0]));
197 1 : GNUNET_assert (GNUNET_OK ==
198 : TALER_string_to_amount ("CHF:4.5",
199 : &aamountc[1]));
200 1 : GNUNET_assert (GNUNET_OK ==
201 : TALER_string_to_amount ("FOO:8.7",
202 : &tamountc));
203 1 : json = json_object ();
204 1 : GNUNET_assert (NULL != json);
205 1 : GNUNET_assert (0 ==
206 : json_object_set_new (json,
207 : "foo",
208 : json_integer (42)));
209 1 : 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 1 : ubs = GNUNET_new (struct GNUNET_CRYPTO_UnblindedSignature);
217 1 : ubs->cipher = GNUNET_CRYPTO_BSA_CS;
218 1 : ubs->rc = 1;
219 1 : GNUNET_CRYPTO_random_block (&ubs->details.cs_signature,
220 : sizeof (ubs->details.cs_signature));
221 1 : denom_sigs[0].unblinded_sig = ubs;
222 :
223 1 : rsa_priv = GNUNET_CRYPTO_rsa_private_key_create (1024);
224 1 : GNUNET_assert (NULL != rsa_priv);
225 1 : ubs = GNUNET_new (struct GNUNET_CRYPTO_UnblindedSignature);
226 1 : ubs->cipher = GNUNET_CRYPTO_BSA_RSA;
227 1 : ubs->rc = 1;
228 : ubs->details.rsa_signature
229 1 : = GNUNET_CRYPTO_rsa_sign_fdh (rsa_priv,
230 : &hc,
231 : sizeof (hc));
232 1 : GNUNET_assert (NULL != ubs->details.rsa_signature);
233 1 : GNUNET_CRYPTO_rsa_private_key_free (rsa_priv);
234 1 : denom_sigs[1].unblinded_sig = ubs;
235 : }
236 : {
237 1 : struct GNUNET_PQ_QueryParam params_insert[] = {
238 1 : TALER_PQ_query_param_amount (conn,
239 : &tamount),
240 1 : TALER_PQ_query_param_json (json),
241 1 : TALER_PQ_query_param_array_amount (3,
242 : aamount,
243 : conn),
244 1 : TALER_PQ_query_param_array_amount_with_currency (2,
245 : aamountc,
246 : "merchant",
247 : conn),
248 1 : TALER_PQ_query_param_amount_with_currency (conn,
249 : &tamountc),
250 1 : GNUNET_PQ_query_param_fixed_size (&hc,
251 : sizeof (hc)),
252 1 : TALER_PQ_query_param_array_hash_code (2,
253 : hcs,
254 : conn),
255 1 : TALER_PQ_query_param_array_cs_r_pub (5,
256 : in_cs_r_pubs,
257 : conn),
258 1 : 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 1 : result = GNUNET_PQ_exec_prepared (conn,
266 : "test_insert",
267 : params_insert);
268 6 : for (uint8_t i = 0; i < 5; i++)
269 : {
270 5 : printf (" in_cs_r_pubs[%d]=%s\n",
271 : i,
272 : GNUNET_STRINGS_data_to_string_alloc (
273 5 : &in_cs_r_pubs[i],
274 : sizeof(in_cs_r_pubs[i])));
275 : }
276 :
277 1 : if (PGRES_COMMAND_OK != PQresultStatus (result))
278 : {
279 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
280 : "Database failure: %s\n",
281 : PQresultErrorMessage (result));
282 0 : PQclear (result);
283 0 : return 1;
284 : }
285 1 : PQclear (result);
286 1 : 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 1 : struct GNUNET_PQ_QueryParam params_select[] = {
306 : GNUNET_PQ_query_param_end
307 : };
308 1 : struct GNUNET_PQ_ResultSpec results_select[] = {
309 1 : TALER_PQ_result_spec_amount ("tamount",
310 : "EUR",
311 : &tamount2),
312 1 : TALER_PQ_result_spec_json ("json",
313 : &json2),
314 1 : TALER_PQ_result_spec_array_amount (conn,
315 : "aamount",
316 : "EUR",
317 : &npamount,
318 : &pamount),
319 1 : TALER_PQ_result_spec_array_amount_with_currency (conn,
320 : "merchant",
321 : "aamountc",
322 : &npamountc,
323 : &pamountc),
324 1 : TALER_PQ_result_spec_array_amount (conn,
325 : "aamountn",
326 : "EUR",
327 : &npamountn,
328 : &pamountn),
329 1 : TALER_PQ_result_spec_array_amount_with_currency (conn,
330 : "merchant",
331 : "aamountnc",
332 : &npamountnc,
333 : &pamountnc),
334 1 : TALER_PQ_result_spec_amount_with_currency ("tamountc",
335 : &tamountc2),
336 1 : GNUNET_PQ_result_spec_auto_from_type ("hash",
337 : &hc2),
338 1 : TALER_PQ_result_spec_array_hash_code (conn,
339 : "hashes",
340 : &nhcs,
341 : &hcs2),
342 1 : 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 1 : if (1 !=
350 1 : GNUNET_PQ_eval_prepared_singleton_select (conn,
351 : "test_select",
352 : params_select,
353 : results_select))
354 : {
355 0 : GNUNET_break (0);
356 0 : return 1;
357 : }
358 1 : GNUNET_break (0 ==
359 : TALER_amount_cmp (&tamount,
360 : &tamount2));
361 1 : GNUNET_break (42 ==
362 : json_integer_value (json_object_get (json2,
363 : "foo")));
364 1 : GNUNET_break (3 == npamount);
365 4 : for (size_t i = 0; i < 3; i++)
366 : {
367 3 : GNUNET_break (0 ==
368 : TALER_amount_cmp (&aamount[i],
369 : &pamount[i]));
370 : }
371 1 : GNUNET_break (2 == npamountc);
372 3 : for (size_t i = 0; i < npamountc; i++)
373 : {
374 2 : GNUNET_break (0 ==
375 : TALER_amount_cmp (&aamountc[i],
376 : &pamountc[i]));
377 : }
378 1 : GNUNET_break (0 ==
379 : TALER_amount_cmp (&tamountc,
380 : &tamountc2));
381 1 : GNUNET_break (0 == GNUNET_memcmp (&hc,&hc2));
382 3 : for (size_t i = 0; i < 2; i++)
383 : {
384 2 : GNUNET_break (0 ==
385 : GNUNET_memcmp (&hcs[i],
386 : &hcs2[i]));
387 : }
388 1 : GNUNET_break (5 == n_rpubs);
389 6 : for (uint8_t i = 0; i < 5; i++)
390 : {
391 5 : GNUNET_break (0 ==
392 : GNUNET_memcmp (&in_cs_r_pubs[i],
393 : &out_cs_r_pubs[i]));
394 5 : printf ("out_cs_r_pubs[%d]=%s\n",
395 : i,
396 : GNUNET_STRINGS_data_to_string_alloc (
397 5 : &out_cs_r_pubs[i],
398 : sizeof(out_cs_r_pubs[i])));
399 : }
400 1 : GNUNET_PQ_cleanup_result (results_select);
401 : }
402 : {
403 1 : struct DenomSigCheck dsc = {
404 : .expected = denom_sigs,
405 : .num = 2
406 : };
407 1 : struct GNUNET_PQ_QueryParam params_select[] = {
408 : GNUNET_PQ_query_param_end
409 : };
410 :
411 1 : if (2 !=
412 1 : GNUNET_PQ_eval_prepared_multi_select (conn,
413 : "test_select_denom_sigs",
414 : params_select,
415 : &denom_sig_cb,
416 : &dsc))
417 : {
418 0 : GNUNET_break (0);
419 0 : return 1;
420 : }
421 1 : GNUNET_break (2 == dsc.seen);
422 1 : GNUNET_break (! dsc.mismatch);
423 1 : if ( (2 != dsc.seen) ||
424 1 : (dsc.mismatch) )
425 0 : return 1;
426 : }
427 1 : TALER_denom_sig_free (&denom_sigs[0]);
428 1 : TALER_denom_sig_free (&denom_sigs[1]);
429 1 : return 0;
430 : }
431 :
432 :
433 : int
434 1 : main (int argc,
435 : const char *const argv[])
436 : {
437 1 : struct GNUNET_PQ_ExecuteStatement es[] = {
438 1 : GNUNET_PQ_make_execute ("DO $$ "
439 : " BEGIN"
440 : " CREATE SCHEMA IF NOT EXISTS merchant;"
441 : " END "
442 : "$$;"),
443 1 : 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 1 : 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 1 : 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 1 : 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 1 : GNUNET_log_setup ("test-pq",
489 : "WARNING",
490 : NULL);
491 1 : cfg = GNUNET_CONFIGURATION_create (
492 : TALER_EXCHANGE_project_data ());
493 1 : GNUNET_CONFIGURATION_set_value_string (cfg,
494 : "test-pq",
495 : "CONFIG",
496 : "postgres:///talercheck");
497 : /* just to squash warning */
498 1 : GNUNET_CONFIGURATION_set_value_string (cfg,
499 : "test-pq",
500 : "SQL_DIR",
501 : "none");
502 1 : conn = GNUNET_PQ_init (cfg,
503 : "test-pq",
504 : NULL,
505 : NULL);
506 1 : GNUNET_CONFIGURATION_destroy (cfg);
507 1 : if (NULL == conn)
508 0 : return 77;
509 1 : GNUNET_assert (GNUNET_OK ==
510 : GNUNET_PQ_exec_statements (conn,
511 : es));
512 1 : if (GNUNET_OK !=
513 1 : postgres_prepare (conn))
514 : {
515 0 : GNUNET_break (0);
516 0 : GNUNET_PQ_disconnect (conn);
517 0 : return 1;
518 : }
519 1 : ret = run_queries (conn);
520 : {
521 1 : struct GNUNET_PQ_ExecuteStatement ds[] = {
522 1 : GNUNET_PQ_make_execute ("DROP TABLE test_pq"),
523 : GNUNET_PQ_EXECUTE_STATEMENT_END
524 : };
525 :
526 1 : if (GNUNET_OK !=
527 1 : GNUNET_PQ_exec_statements (conn,
528 : ds))
529 : {
530 0 : fprintf (stderr,
531 : "Failed to drop table\n");
532 0 : GNUNET_PQ_disconnect (conn);
533 0 : return 1;
534 : }
535 : }
536 1 : GNUNET_PQ_disconnect (conn);
537 1 : return ret;
538 : }
539 :
540 :
541 : /* end of test_pq.c */
|