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