Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 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 exchangedb/test_common.c
18 : * @brief shared scaffolding for the per-table exchangedb tests
19 : * @author Christian Grothoff
20 : */
21 : #include "test_common.h"
22 : #include "exchange-database/create_tables.h"
23 : #include "exchange-database/do_insert_known_coin.h"
24 : #include "exchange-database/insert_denomination_info.h"
25 : #include "exchange-database/get_denomination_info.h"
26 : #include "exchange-database/do_deposit.h"
27 : #include "exchange-database/insert_purse_request.h"
28 : #include "exchange-database/do_withdraw.h"
29 :
30 :
31 : /**
32 : * Name of the single check to run, NULL to run all of them.
33 : */
34 : static char *only;
35 :
36 : /**
37 : * Return value of the process.
38 : */
39 : static int result;
40 :
41 : /**
42 : * Checks the current binary wants to run.
43 : */
44 : static const struct TDB_Test *tdb_tests;
45 :
46 :
47 : enum GNUNET_GenericReturnValue
48 105 : TDB_exec (struct TALER_EXCHANGEDB_PostgresContext *pg,
49 : const char *fmt,
50 : ...)
51 : {
52 : enum GNUNET_GenericReturnValue ret;
53 : va_list ap;
54 : char *sql;
55 : int len;
56 :
57 105 : va_start (ap,
58 : fmt);
59 105 : len = vsnprintf (NULL,
60 : 0,
61 : fmt,
62 : ap);
63 105 : va_end (ap);
64 105 : GNUNET_assert (len >= 0);
65 105 : sql = GNUNET_malloc ((size_t) len + 1);
66 105 : va_start (ap,
67 : fmt);
68 105 : GNUNET_assert (len ==
69 : vsnprintf (sql,
70 : (size_t) len + 1,
71 : fmt,
72 : ap));
73 105 : va_end (ap);
74 : {
75 105 : struct GNUNET_PQ_ExecuteStatement es[] = {
76 105 : GNUNET_PQ_make_execute (sql),
77 : GNUNET_PQ_EXECUTE_STATEMENT_END
78 : };
79 :
80 105 : ret = GNUNET_PQ_exec_statements (pg->conn,
81 : es);
82 : }
83 105 : GNUNET_free (sql);
84 105 : return ret;
85 : }
86 :
87 :
88 : uint64_t
89 227 : TDB_count (struct TALER_EXCHANGEDB_PostgresContext *pg,
90 : const char *fmt,
91 : ...)
92 : {
93 227 : struct GNUNET_PQ_QueryParam params[] = {
94 : GNUNET_PQ_query_param_end
95 : };
96 : uint64_t n;
97 227 : struct GNUNET_PQ_ResultSpec rs[] = {
98 227 : GNUNET_PQ_result_spec_uint64 ("n",
99 : &n),
100 : GNUNET_PQ_result_spec_end
101 : };
102 : va_list ap;
103 : char *frag;
104 : char *sql;
105 : int len;
106 :
107 227 : va_start (ap,
108 : fmt);
109 227 : len = vsnprintf (NULL,
110 : 0,
111 : fmt,
112 : ap);
113 227 : va_end (ap);
114 227 : GNUNET_assert (len >= 0);
115 227 : frag = GNUNET_malloc ((size_t) len + 1);
116 227 : va_start (ap,
117 : fmt);
118 227 : GNUNET_assert (len ==
119 : vsnprintf (frag,
120 : (size_t) len + 1,
121 : fmt,
122 : ap));
123 227 : va_end (ap);
124 227 : GNUNET_asprintf (&sql,
125 : "SELECT COUNT(*) AS n %s;",
126 : frag);
127 227 : GNUNET_free (frag);
128 227 : GNUNET_assert (GNUNET_OK ==
129 : GNUNET_PQ_prepare_anon (pg->conn,
130 : sql));
131 227 : GNUNET_free (sql);
132 227 : GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
133 : GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
134 : "",
135 : params,
136 : rs));
137 227 : return n;
138 : }
139 :
140 :
141 : struct TALER_Amount
142 385 : TDB_amount (const char *str)
143 : {
144 : struct TALER_Amount amount;
145 : char *s;
146 :
147 385 : GNUNET_asprintf (&s,
148 : CURRENCY ":%s",
149 : str);
150 385 : GNUNET_assert (GNUNET_OK ==
151 : TALER_string_to_amount (s,
152 : &amount));
153 385 : GNUNET_free (s);
154 385 : return amount;
155 : }
156 :
157 :
158 : void
159 726 : TDB_fill (void *ptr,
160 : size_t size,
161 : uint32_t seed)
162 : {
163 726 : unsigned char *buf = ptr;
164 726 : uint32_t state = seed * 2654435761U + 1;
165 :
166 35934 : for (size_t i = 0; i<size; i++)
167 : {
168 35208 : state = state * 1103515245U + 12345U;
169 35208 : buf[i] = (unsigned char) (state >> 16);
170 : }
171 726 : }
172 :
173 :
174 : char *
175 163 : TDB_hex (const void *data,
176 : size_t size)
177 : {
178 163 : const unsigned char *raw = data;
179 : char *hex;
180 :
181 163 : hex = GNUNET_malloc (size * 2 + 1);
182 5379 : for (size_t i = 0; i<size; i++)
183 5216 : GNUNET_snprintf (&hex[i * 2],
184 : 3,
185 : "%02x",
186 5216 : raw[i]);
187 163 : return hex;
188 : }
189 :
190 :
191 : void
192 41 : TDB_denom_sig (uint32_t seed,
193 : struct TALER_DenominationSignature *sig)
194 : {
195 : struct GNUNET_CRYPTO_UnblindedSignature *ubs;
196 :
197 41 : ubs = GNUNET_new (struct GNUNET_CRYPTO_UnblindedSignature);
198 41 : ubs->cipher = GNUNET_CRYPTO_BSA_CS;
199 41 : ubs->rc = 1;
200 41 : TDB_fill (&ubs->details.cs_signature,
201 : sizeof (ubs->details.cs_signature),
202 : seed);
203 41 : sig->unblinded_sig = ubs;
204 41 : }
205 :
206 :
207 : void
208 22 : TDB_blinded_denom_sig (uint32_t seed,
209 : struct TALER_BlindedDenominationSignature *sig)
210 : {
211 : struct GNUNET_CRYPTO_BlindedSignature *bs;
212 :
213 22 : bs = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature);
214 22 : bs->cipher = GNUNET_CRYPTO_BSA_CS;
215 22 : bs->rc = 1;
216 22 : TDB_fill (&bs->details.blinded_cs_answer,
217 : sizeof (bs->details.blinded_cs_answer),
218 : seed);
219 22 : sig->blinded_sig = bs;
220 22 : }
221 :
222 :
223 : void
224 32 : TDB_denom (struct TALER_EXCHANGEDB_PostgresContext *pg,
225 : uint32_t seed,
226 : const char *value,
227 : const char *fee,
228 : struct TDB_Denom *denom)
229 : {
230 : struct GNUNET_TIME_Timestamp now;
231 : struct TALER_Amount f;
232 :
233 32 : memset (denom,
234 : 0,
235 : sizeof (*denom));
236 : /* Build the Clause-Schnorr key pair from @a seed instead of at random, so
237 : that two calls with the same seed name the same denomination -- checks
238 : rely on that to refer to a row an earlier check created. The top nibble
239 : of the scalar is cleared to keep it below the group order. */
240 : {
241 : struct GNUNET_CRYPTO_BlindSignPrivateKey *bpriv;
242 : struct GNUNET_CRYPTO_BlindSignPublicKey *bpub;
243 : unsigned char *d;
244 :
245 32 : bpriv = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPrivateKey);
246 32 : bpriv->cipher = GNUNET_CRYPTO_BSA_CS;
247 32 : bpriv->rc = 1;
248 32 : d = bpriv->details.cs_private_key.scalar.d;
249 32 : TDB_fill (d,
250 : sizeof (bpriv->details.cs_private_key.scalar.d),
251 : seed);
252 32 : d[sizeof (bpriv->details.cs_private_key.scalar.d) - 1] &= 0x0f;
253 32 : bpub = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey);
254 32 : bpub->cipher = GNUNET_CRYPTO_BSA_CS;
255 32 : bpub->rc = 1;
256 32 : GNUNET_CRYPTO_cs_private_key_get_public (
257 32 : &bpriv->details.cs_private_key,
258 : &bpub->details.cs_public_key);
259 32 : GNUNET_CRYPTO_hash (&bpub->details.cs_public_key,
260 : sizeof (bpub->details.cs_public_key),
261 : &bpub->pub_key_hash);
262 32 : denom->priv.bsign_priv_key = bpriv;
263 32 : denom->pub.bsign_pub_key = bpub;
264 : }
265 32 : TALER_denom_pub_hash (&denom->pub,
266 : &denom->h_denom_pub);
267 : /* The same seed may be used by more than one check to name the same
268 : denomination; the second call then only has to load it. */
269 32 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
270 32 : TALER_EXCHANGEDB_get_denomination_info (pg,
271 32 : &denom->h_denom_pub,
272 : &denom->serial,
273 : &denom->issue))
274 6 : return;
275 26 : now = GNUNET_TIME_timestamp_get ();
276 26 : f = TDB_amount (fee);
277 26 : denom->issue.start = now;
278 : denom->issue.expire_withdraw
279 26 : = GNUNET_TIME_absolute_to_timestamp (
280 : GNUNET_TIME_absolute_add (now.abs_time,
281 : GNUNET_TIME_UNIT_HOURS));
282 : denom->issue.expire_deposit
283 26 : = GNUNET_TIME_absolute_to_timestamp (
284 : GNUNET_TIME_absolute_add (now.abs_time,
285 : GNUNET_TIME_relative_multiply (
286 : GNUNET_TIME_UNIT_HOURS,
287 : 2)));
288 : denom->issue.expire_legal
289 26 : = GNUNET_TIME_absolute_to_timestamp (
290 : GNUNET_TIME_absolute_add (now.abs_time,
291 : GNUNET_TIME_relative_multiply (
292 : GNUNET_TIME_UNIT_HOURS,
293 : 3)));
294 26 : denom->issue.value = TDB_amount (value);
295 26 : denom->issue.fees.withdraw = f;
296 26 : denom->issue.fees.deposit = f;
297 26 : denom->issue.fees.refresh = f;
298 26 : denom->issue.fees.refund = f;
299 26 : denom->issue.denom_hash = denom->h_denom_pub;
300 26 : TDB_fill (&denom->issue.signature,
301 : sizeof (denom->issue.signature),
302 : seed);
303 26 : GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
304 : TALER_EXCHANGEDB_insert_denomination_info (pg,
305 : &denom->pub,
306 : &denom->issue));
307 : {
308 26 : struct GNUNET_PQ_QueryParam params[] = {
309 26 : GNUNET_PQ_query_param_auto_from_type (&denom->h_denom_pub),
310 : GNUNET_PQ_query_param_end
311 : };
312 26 : struct GNUNET_PQ_ResultSpec rs[] = {
313 26 : GNUNET_PQ_result_spec_uint64 ("denominations_serial",
314 : &denom->serial),
315 : GNUNET_PQ_result_spec_end
316 : };
317 :
318 26 : GNUNET_assert (GNUNET_OK ==
319 : GNUNET_PQ_prepare_anon (
320 : pg->conn,
321 : "SELECT denominations_serial"
322 : " FROM denominations"
323 : " WHERE denom_pub_hash=$1;"));
324 26 : GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
325 : GNUNET_PQ_eval_prepared_singleton_select (
326 : pg->conn,
327 : "",
328 : params,
329 : rs));
330 : }
331 : }
332 :
333 :
334 : void
335 32 : TDB_denom_free (struct TDB_Denom *denom)
336 : {
337 32 : TALER_denom_pub_free (&denom->pub);
338 32 : TALER_denom_priv_free (&denom->priv);
339 32 : }
340 :
341 :
342 : void
343 38 : TDB_coin (struct TALER_EXCHANGEDB_PostgresContext *pg,
344 : const struct TDB_Denom *denom,
345 : uint32_t seed,
346 : struct TALER_CoinPublicInfo *coin,
347 : uint64_t *known_coin_id)
348 : {
349 : struct TALER_DenominationHashP dh;
350 : struct TALER_AgeCommitmentHashP hac;
351 : enum TALER_EXCHANGEDB_CoinKnownStatus cks;
352 : uint64_t kci;
353 :
354 38 : memset (coin,
355 : 0,
356 : sizeof (*coin));
357 38 : TDB_FILL (coin->coin_pub,
358 : seed);
359 38 : coin->denom_pub_hash = denom->h_denom_pub;
360 38 : coin->no_age_commitment = true;
361 38 : TDB_denom_sig (seed,
362 : &coin->denom_sig);
363 38 : cks = TALER_EXCHANGEDB_do_insert_known_coin (pg,
364 : coin,
365 : &kci,
366 : &dh,
367 : &hac);
368 : /* CKS_PRESENT is fine: the same seed may name a coin an earlier check
369 : already made known. */
370 38 : GNUNET_assert ( (TALER_EXCHANGEDB_CKS_ADDED == cks) ||
371 : (TALER_EXCHANGEDB_CKS_PRESENT == cks) );
372 38 : if (NULL != known_coin_id)
373 9 : *known_coin_id = kci;
374 38 : }
375 :
376 :
377 : void
378 38 : TDB_coin_free (struct TALER_CoinPublicInfo *coin)
379 : {
380 38 : TALER_denom_sig_free (&coin->denom_sig);
381 38 : }
382 :
383 :
384 : void
385 36 : TDB_account (struct TALER_EXCHANGEDB_PostgresContext *pg,
386 : uint32_t seed,
387 : struct TDB_Account *account)
388 : {
389 : struct TALER_NormalizedPayto npayto;
390 : char *fhex;
391 : char *nhex;
392 :
393 36 : GNUNET_asprintf (&account->payto.full_payto,
394 : "payto://x-taler-bank/localhost/tdb%u?receiver-name=tdb%u",
395 : (unsigned int) seed,
396 : (unsigned int) seed);
397 36 : TALER_full_payto_hash (account->payto,
398 : &account->h_full);
399 36 : npayto = TALER_payto_normalize (account->payto);
400 36 : GNUNET_assert (NULL != npayto.normalized_payto);
401 36 : TALER_normalized_payto_hash (npayto,
402 : &account->h_normalized);
403 36 : GNUNET_free (npayto.normalized_payto);
404 36 : fhex = TDB_hex (&account->h_full,
405 : sizeof (account->h_full));
406 36 : nhex = TDB_hex (&account->h_normalized,
407 : sizeof (account->h_normalized));
408 36 : GNUNET_assert (GNUNET_OK ==
409 : TDB_exec (pg,
410 : "INSERT INTO kyc_targets"
411 : " (h_normalized_payto,is_wallet)"
412 : " VALUES (decode('%s','hex'),FALSE)"
413 : " ON CONFLICT DO NOTHING;"
414 : "INSERT INTO wire_targets"
415 : " (wire_target_h_payto,payto_uri"
416 : " ,h_normalized_payto)"
417 : " VALUES (decode('%s','hex'),'%s'"
418 : " ,decode('%s','hex'))"
419 : " ON CONFLICT DO NOTHING;",
420 : nhex,
421 : fhex,
422 : account->payto.full_payto,
423 : nhex));
424 36 : GNUNET_free (fhex);
425 36 : GNUNET_free (nhex);
426 36 : }
427 :
428 :
429 : void
430 36 : TDB_account_free (struct TDB_Account *account)
431 : {
432 36 : GNUNET_free (account->payto.full_payto);
433 36 : }
434 :
435 :
436 : void
437 32 : TDB_reserve (struct TALER_EXCHANGEDB_PostgresContext *pg,
438 : uint32_t seed,
439 : const char *balance,
440 : struct TALER_ReservePublicKeyP *reserve_pub)
441 : {
442 32 : struct TALER_Amount b = TDB_amount (balance);
443 : char *hex;
444 :
445 32 : TDB_fill (reserve_pub,
446 : sizeof (*reserve_pub),
447 : seed);
448 32 : hex = TDB_hex (reserve_pub,
449 : sizeof (*reserve_pub));
450 32 : GNUNET_assert (GNUNET_OK ==
451 : TDB_exec (pg,
452 : "INSERT INTO reserves"
453 : " (reserve_pub,current_balance"
454 : " ,expiration_date,gc_date)"
455 : " VALUES (decode('%s','hex')"
456 : " ,ROW(%llu,%u)::taler_amount"
457 : " ,%llu,%llu)"
458 : " ON CONFLICT DO NOTHING;",
459 : hex,
460 : (unsigned long long) b.value,
461 : (unsigned int) b.fraction,
462 : (unsigned long long) 4000000000000000LLU,
463 : (unsigned long long) 5000000000000000LLU));
464 32 : GNUNET_free (hex);
465 32 : }
466 :
467 :
468 : void
469 19 : TDB_purse (struct TALER_EXCHANGEDB_PostgresContext *pg,
470 : uint32_t seed,
471 : const char *amount,
472 : struct GNUNET_TIME_Timestamp purse_expiration,
473 : struct TDB_Purse *purse)
474 : {
475 19 : struct TALER_Amount purse_fee = TDB_amount ("0");
476 : enum GNUNET_DB_QueryStatus qs;
477 19 : bool in_conflict = false;
478 :
479 19 : memset (purse,
480 : 0,
481 : sizeof (*purse));
482 19 : TDB_fill (&purse->purse_pub,
483 : sizeof (purse->purse_pub),
484 : seed);
485 19 : TDB_fill (&purse->merge_pub,
486 : sizeof (purse->merge_pub),
487 : seed);
488 19 : TDB_fill (&purse->h_contract_terms,
489 : sizeof (purse->h_contract_terms),
490 : seed);
491 19 : TDB_fill (&purse->purse_sig,
492 : sizeof (purse->purse_sig),
493 : seed);
494 19 : purse->purse_expiration = purse_expiration;
495 19 : purse->amount = TDB_amount (amount);
496 19 : qs = TALER_EXCHANGEDB_insert_purse_request (
497 : pg,
498 19 : &purse->purse_pub,
499 19 : &purse->merge_pub,
500 : purse_expiration,
501 19 : &purse->h_contract_terms,
502 : 0,
503 : TALER_WAMF_MODE_CREATE_FROM_PURSE_QUOTA,
504 : &purse_fee,
505 19 : &purse->amount,
506 19 : &purse->purse_sig,
507 : &in_conflict);
508 : /* SUCCESS_NO_RESULTS is the answer for an identical replay, which is
509 : what a second call with the same seed is. */
510 19 : GNUNET_assert (0 <= qs);
511 19 : GNUNET_assert (! in_conflict);
512 19 : }
513 :
514 :
515 : void
516 12 : TDB_deposit (struct TALER_EXCHANGEDB_PostgresContext *pg,
517 : const struct TDB_Account *account,
518 : const struct TALER_CoinPublicInfo *coin,
519 : uint32_t seed,
520 : const char *amount,
521 : const char *fee,
522 : struct GNUNET_TIME_Timestamp wire_deadline,
523 : struct GNUNET_TIME_Timestamp refund_deadline,
524 : struct TDB_Deposit *dep)
525 : {
526 : struct TALER_EXCHANGEDB_CoinDepositInformation cdi;
527 : struct TALER_EXCHANGEDB_BatchDeposit bd;
528 : struct TALER_Amount fees[1];
529 : struct TALER_Amount total;
530 : uint32_t bad_balance_index;
531 : bool balance_ok;
532 : bool in_conflict;
533 :
534 12 : memset (dep,
535 : 0,
536 : sizeof (*dep));
537 12 : TDB_fill (&dep->merchant_pub,
538 : sizeof (dep->merchant_pub),
539 : seed);
540 12 : TDB_fill (&dep->merchant_sig,
541 : sizeof (dep->merchant_sig),
542 : seed);
543 12 : TDB_fill (&dep->h_contract_terms,
544 : sizeof (dep->h_contract_terms),
545 : seed);
546 12 : TDB_fill (&dep->wire_salt,
547 : sizeof (dep->wire_salt),
548 : seed);
549 12 : TALER_merchant_wire_signature_hash (account->payto,
550 12 : &dep->wire_salt,
551 : &dep->h_wire);
552 12 : dep->wire_deadline = wire_deadline;
553 12 : dep->refund_deadline = refund_deadline;
554 12 : memset (&cdi,
555 : 0,
556 : sizeof (cdi));
557 12 : cdi.coin = *coin;
558 12 : TDB_fill (&cdi.csig,
559 : sizeof (cdi.csig),
560 : seed);
561 12 : cdi.amount_with_fee = TDB_amount (amount);
562 12 : fees[0] = TDB_amount (fee);
563 12 : memset (&bd,
564 : 0,
565 : sizeof (bd));
566 12 : bd.merchant_pub = dep->merchant_pub;
567 12 : bd.merchant_sig = dep->merchant_sig;
568 12 : bd.h_contract_terms = dep->h_contract_terms;
569 12 : bd.no_wallet_data_hash = true;
570 12 : bd.wire_target_h_payto = account->h_full;
571 12 : bd.wire_salt = dep->wire_salt;
572 12 : bd.wallet_timestamp = wire_deadline;
573 12 : bd.refund_deadline = refund_deadline;
574 12 : bd.wire_deadline = wire_deadline;
575 12 : bd.receiver_wire_account = account->payto;
576 12 : bd.cdis = &cdi;
577 12 : bd.num_cdis = 1;
578 12 : dep->exchange_timestamp = GNUNET_TIME_timestamp_get ();
579 12 : GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
580 : TALER_EXCHANGEDB_do_deposit (pg,
581 : &bd,
582 : fees,
583 : &dep->exchange_timestamp,
584 : &total,
585 : &balance_ok,
586 : &bad_balance_index,
587 : &in_conflict));
588 12 : GNUNET_assert (balance_ok);
589 12 : GNUNET_assert (! in_conflict);
590 : {
591 12 : struct GNUNET_PQ_QueryParam params[] = {
592 12 : GNUNET_PQ_query_param_auto_from_type (&dep->merchant_pub),
593 12 : GNUNET_PQ_query_param_auto_from_type (&dep->h_contract_terms),
594 : GNUNET_PQ_query_param_end
595 : };
596 12 : struct GNUNET_PQ_ResultSpec rs[] = {
597 12 : GNUNET_PQ_result_spec_uint64 ("batch_deposit_serial_id",
598 : &dep->serial),
599 : GNUNET_PQ_result_spec_end
600 : };
601 :
602 12 : GNUNET_assert (GNUNET_OK ==
603 : GNUNET_PQ_prepare_anon (pg->conn,
604 : "SELECT batch_deposit_serial_id"
605 : " FROM batch_deposits"
606 : " WHERE merchant_pub=$1"
607 : " AND h_contract_terms=$2;"));
608 12 : GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
609 : GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
610 : "",
611 : params,
612 : rs));
613 : }
614 12 : }
615 :
616 :
617 : uint64_t
618 5 : TDB_withdraw (struct TALER_EXCHANGEDB_PostgresContext *pg,
619 : const struct TDB_Denom *denom,
620 : const struct TALER_ReservePublicKeyP *reserve_pub,
621 : uint32_t seed,
622 : const char *amount)
623 : {
624 : struct TALER_EXCHANGEDB_Withdraw wd;
625 5 : struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
626 : struct TALER_Amount reserve_balance;
627 : struct TALER_BlindedDenominationSignature denom_sig;
628 5 : uint64_t denom_serial = denom->serial;
629 : uint64_t withdraw_id;
630 : uint16_t allowed_maximum_age;
631 : uint16_t noreveal_index;
632 : uint32_t reserve_birthday;
633 : bool balance_ok;
634 : bool age_ok;
635 : bool idempotent;
636 : bool nonce_reuse;
637 :
638 5 : memset (&wd,
639 : 0,
640 : sizeof (wd));
641 5 : wd.amount_with_fee = TDB_amount (amount);
642 5 : wd.reserve_pub = *reserve_pub;
643 5 : TDB_fill (&wd.planchets_h,
644 : sizeof (wd.planchets_h),
645 : seed);
646 5 : TDB_fill (&wd.reserve_sig,
647 : sizeof (wd.reserve_sig),
648 : seed);
649 5 : wd.num_coins = 1;
650 5 : wd.denom_serials = &denom_serial;
651 5 : TDB_blinded_denom_sig (seed,
652 : &denom_sig);
653 5 : wd.denom_sigs = &denom_sig;
654 5 : wd.no_blinding_seed = true;
655 5 : GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
656 : TALER_EXCHANGEDB_do_withdraw (pg,
657 : &wd,
658 : &now,
659 : &balance_ok,
660 : &reserve_balance,
661 : &age_ok,
662 : &allowed_maximum_age,
663 : &reserve_birthday,
664 : &idempotent,
665 : &noreveal_index,
666 : &nonce_reuse));
667 5 : GNUNET_assert (balance_ok);
668 5 : GNUNET_assert (age_ok);
669 5 : GNUNET_assert (! nonce_reuse);
670 5 : TALER_blinded_denom_sig_free (&denom_sig);
671 : {
672 5 : struct GNUNET_PQ_QueryParam params[] = {
673 5 : GNUNET_PQ_query_param_auto_from_type (&wd.planchets_h),
674 : GNUNET_PQ_query_param_end
675 : };
676 5 : struct GNUNET_PQ_ResultSpec rs[] = {
677 5 : GNUNET_PQ_result_spec_uint64 ("withdraw_id",
678 : &withdraw_id),
679 : GNUNET_PQ_result_spec_end
680 : };
681 :
682 5 : GNUNET_assert (GNUNET_OK ==
683 : GNUNET_PQ_prepare_anon (pg->conn,
684 : "SELECT withdraw_id"
685 : " FROM withdraw"
686 : " WHERE planchets_h=$1;"));
687 5 : GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
688 : GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
689 : "",
690 : params,
691 : rs));
692 : }
693 5 : return withdraw_id;
694 : }
695 :
696 :
697 : uint64_t
698 12 : TDB_reserve_in (struct TALER_EXCHANGEDB_PostgresContext *pg,
699 : const struct TDB_Account *account,
700 : uint32_t seed,
701 : const char *amount,
702 : struct TALER_ReservePublicKeyP *reserve_pub)
703 : {
704 12 : struct TALER_Amount a = TDB_amount (amount);
705 : uint64_t serial;
706 : char *rhex;
707 : char *ahex;
708 :
709 12 : TDB_reserve (pg,
710 : seed,
711 : amount,
712 : reserve_pub);
713 12 : rhex = TDB_hex (reserve_pub,
714 : sizeof (*reserve_pub));
715 12 : ahex = TDB_hex (&account->h_full,
716 : sizeof (account->h_full));
717 12 : GNUNET_assert (GNUNET_OK ==
718 : TDB_exec (pg,
719 : "INSERT INTO reserves_in"
720 : " (reserve_pub,wire_reference,credit"
721 : " ,wire_source_h_payto"
722 : " ,exchange_account_section,execution_date)"
723 : " VALUES (decode('%s','hex'),%u"
724 : " ,ROW(%llu,%u)::taler_amount"
725 : " ,decode('%s','hex'),'exchange-account-1'"
726 : " ,%llu);",
727 : rhex,
728 : (unsigned int) seed,
729 : (unsigned long long) a.value,
730 : (unsigned int) a.fraction,
731 : ahex,
732 : (unsigned long long) 1600000000000000LLU));
733 : {
734 12 : struct GNUNET_PQ_QueryParam params[] = {
735 12 : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
736 : GNUNET_PQ_query_param_end
737 : };
738 12 : struct GNUNET_PQ_ResultSpec rs[] = {
739 12 : GNUNET_PQ_result_spec_uint64 ("reserve_in_serial_id",
740 : &serial),
741 : GNUNET_PQ_result_spec_end
742 : };
743 :
744 12 : GNUNET_assert (GNUNET_OK ==
745 : GNUNET_PQ_prepare_anon (
746 : pg->conn,
747 : "SELECT reserve_in_serial_id"
748 : " FROM reserves_in"
749 : " WHERE reserve_pub=$1;"));
750 12 : GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
751 : GNUNET_PQ_eval_prepared_singleton_select (
752 : pg->conn,
753 : "",
754 : params,
755 : rs));
756 : }
757 12 : GNUNET_free (rhex);
758 12 : GNUNET_free (ahex);
759 12 : return serial;
760 : }
761 :
762 :
763 : /**
764 : * Main function that runs the checks.
765 : *
766 : * @param cls closure
767 : * @param args remaining command-line arguments
768 : * @param cfgfile name of the configuration file used
769 : * @param cfg configuration
770 : */
771 : static void
772 53 : run (void *cls,
773 : char *const *args,
774 : const char *cfgfile,
775 : const struct GNUNET_CONFIGURATION_Handle *cfg)
776 : {
777 : struct TALER_EXCHANGEDB_PostgresContext *pg;
778 53 : unsigned int ran = 0;
779 :
780 : (void) cls;
781 : (void) args;
782 : (void) cfgfile;
783 53 : pg = TALER_EXCHANGEDB_connect_admin (cfg);
784 53 : if (NULL == pg)
785 : {
786 0 : fprintf (stderr,
787 : "Failed to connect to the database\n");
788 0 : result = 77;
789 0 : return;
790 : }
791 53 : if (GNUNET_OK !=
792 53 : TALER_EXCHANGEDB_create_tables (pg,
793 : false,
794 : 0))
795 : {
796 0 : fprintf (stderr,
797 : "Failed to create the database schema\n");
798 0 : result = 77;
799 0 : goto cleanup;
800 : }
801 302 : for (unsigned int i = 0; NULL != tdb_tests[i].name; i++)
802 : {
803 249 : if ( (NULL != only) &&
804 0 : (0 != strcmp (only,
805 0 : tdb_tests[i].name)) )
806 0 : continue;
807 249 : fprintf (stderr,
808 : "Running check `%s'\n",
809 249 : tdb_tests[i].name);
810 249 : ran++;
811 249 : if (0 != tdb_tests[i].fn (pg))
812 : {
813 0 : fprintf (stderr,
814 : "Check `%s' FAILED\n",
815 0 : tdb_tests[i].name);
816 0 : result = 1;
817 : }
818 : }
819 53 : if (0 == ran)
820 : {
821 0 : fprintf (stderr,
822 : "No check matched `%s'\n",
823 : only);
824 0 : result = 1;
825 : }
826 53 : cleanup:
827 53 : TALER_EXCHANGEDB_disconnect (pg);
828 : }
829 :
830 :
831 : int
832 53 : TDB_main (int argc,
833 : char *const *argv,
834 : const char *binary,
835 : const char *description,
836 : const struct TDB_Test *tests)
837 : {
838 53 : struct GNUNET_GETOPT_CommandLineOption options[] = {
839 53 : GNUNET_GETOPT_option_string ('t',
840 : "test",
841 : "NAME",
842 : "only run the check called NAME",
843 : &only),
844 : GNUNET_GETOPT_OPTION_END
845 : };
846 : enum GNUNET_GenericReturnValue ret;
847 :
848 53 : result = 0;
849 53 : tdb_tests = tests;
850 53 : ret = GNUNET_PROGRAM_run (TALER_EXCHANGE_project_data (),
851 : argc,
852 : argv,
853 : binary,
854 : description,
855 : options,
856 : &run,
857 : NULL);
858 53 : if (GNUNET_SYSERR == ret)
859 0 : return 3;
860 53 : if (GNUNET_NO == ret)
861 0 : return 0;
862 53 : return result;
863 : }
864 :
865 :
866 : /* end of test_common.c */
|