test_common.c (27146B)
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 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 va_start (ap, 58 fmt); 59 len = vsnprintf (NULL, 60 0, 61 fmt, 62 ap); 63 va_end (ap); 64 GNUNET_assert (len >= 0); 65 sql = GNUNET_malloc ((size_t) len + 1); 66 va_start (ap, 67 fmt); 68 GNUNET_assert (len == 69 vsnprintf (sql, 70 (size_t) len + 1, 71 fmt, 72 ap)); 73 va_end (ap); 74 { 75 struct GNUNET_PQ_ExecuteStatement es[] = { 76 GNUNET_PQ_make_execute (sql), 77 GNUNET_PQ_EXECUTE_STATEMENT_END 78 }; 79 80 ret = GNUNET_PQ_exec_statements (pg->conn, 81 es); 82 } 83 GNUNET_free (sql); 84 return ret; 85 } 86 87 88 uint64_t 89 TDB_count (struct TALER_EXCHANGEDB_PostgresContext *pg, 90 const char *fmt, 91 ...) 92 { 93 struct GNUNET_PQ_QueryParam params[] = { 94 GNUNET_PQ_query_param_end 95 }; 96 uint64_t n; 97 struct GNUNET_PQ_ResultSpec rs[] = { 98 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 va_start (ap, 108 fmt); 109 len = vsnprintf (NULL, 110 0, 111 fmt, 112 ap); 113 va_end (ap); 114 GNUNET_assert (len >= 0); 115 frag = GNUNET_malloc ((size_t) len + 1); 116 va_start (ap, 117 fmt); 118 GNUNET_assert (len == 119 vsnprintf (frag, 120 (size_t) len + 1, 121 fmt, 122 ap)); 123 va_end (ap); 124 GNUNET_asprintf (&sql, 125 "SELECT COUNT(*) AS n %s;", 126 frag); 127 GNUNET_free (frag); 128 GNUNET_assert (GNUNET_OK == 129 GNUNET_PQ_prepare_anon (pg->conn, 130 sql)); 131 GNUNET_free (sql); 132 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 133 GNUNET_PQ_eval_prepared_singleton_select (pg->conn, 134 "", 135 params, 136 rs)); 137 return n; 138 } 139 140 141 struct TALER_Amount 142 TDB_amount (const char *str) 143 { 144 struct TALER_Amount amount; 145 char *s; 146 147 GNUNET_asprintf (&s, 148 CURRENCY ":%s", 149 str); 150 GNUNET_assert (GNUNET_OK == 151 TALER_string_to_amount (s, 152 &amount)); 153 GNUNET_free (s); 154 return amount; 155 } 156 157 158 void 159 TDB_fill (void *ptr, 160 size_t size, 161 uint32_t seed) 162 { 163 unsigned char *buf = ptr; 164 uint32_t state = seed * 2654435761U + 1; 165 166 for (size_t i = 0; i<size; i++) 167 { 168 state = state * 1103515245U + 12345U; 169 buf[i] = (unsigned char) (state >> 16); 170 } 171 } 172 173 174 char * 175 TDB_hex (const void *data, 176 size_t size) 177 { 178 const unsigned char *raw = data; 179 char *hex; 180 181 hex = GNUNET_malloc (size * 2 + 1); 182 for (size_t i = 0; i<size; i++) 183 GNUNET_snprintf (&hex[i * 2], 184 3, 185 "%02x", 186 raw[i]); 187 return hex; 188 } 189 190 191 void 192 TDB_denom_sig (uint32_t seed, 193 struct TALER_DenominationSignature *sig) 194 { 195 struct GNUNET_CRYPTO_UnblindedSignature *ubs; 196 197 ubs = GNUNET_new (struct GNUNET_CRYPTO_UnblindedSignature); 198 ubs->cipher = GNUNET_CRYPTO_BSA_CS; 199 ubs->rc = 1; 200 TDB_fill (&ubs->details.cs_signature, 201 sizeof (ubs->details.cs_signature), 202 seed); 203 sig->unblinded_sig = ubs; 204 } 205 206 207 void 208 TDB_blinded_denom_sig (uint32_t seed, 209 struct TALER_BlindedDenominationSignature *sig) 210 { 211 struct GNUNET_CRYPTO_BlindedSignature *bs; 212 213 bs = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); 214 bs->cipher = GNUNET_CRYPTO_BSA_CS; 215 bs->rc = 1; 216 TDB_fill (&bs->details.blinded_cs_answer, 217 sizeof (bs->details.blinded_cs_answer), 218 seed); 219 sig->blinded_sig = bs; 220 } 221 222 223 void 224 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 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 bpriv = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPrivateKey); 246 bpriv->cipher = GNUNET_CRYPTO_BSA_CS; 247 bpriv->rc = 1; 248 d = bpriv->details.cs_private_key.scalar.d; 249 TDB_fill (d, 250 sizeof (bpriv->details.cs_private_key.scalar.d), 251 seed); 252 d[sizeof (bpriv->details.cs_private_key.scalar.d) - 1] &= 0x0f; 253 bpub = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey); 254 bpub->cipher = GNUNET_CRYPTO_BSA_CS; 255 bpub->rc = 1; 256 GNUNET_CRYPTO_cs_private_key_get_public ( 257 &bpriv->details.cs_private_key, 258 &bpub->details.cs_public_key); 259 GNUNET_CRYPTO_hash (&bpub->details.cs_public_key, 260 sizeof (bpub->details.cs_public_key), 261 &bpub->pub_key_hash); 262 denom->priv.bsign_priv_key = bpriv; 263 denom->pub.bsign_pub_key = bpub; 264 } 265 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 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 270 TALER_EXCHANGEDB_get_denomination_info (pg, 271 &denom->h_denom_pub, 272 &denom->serial, 273 &denom->issue)) 274 return; 275 now = GNUNET_TIME_timestamp_get (); 276 f = TDB_amount (fee); 277 denom->issue.start = now; 278 denom->issue.expire_withdraw 279 = GNUNET_TIME_absolute_to_timestamp ( 280 GNUNET_TIME_absolute_add (now.abs_time, 281 GNUNET_TIME_UNIT_HOURS)); 282 denom->issue.expire_deposit 283 = 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 = 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 denom->issue.value = TDB_amount (value); 295 denom->issue.fees.withdraw = f; 296 denom->issue.fees.deposit = f; 297 denom->issue.fees.refresh = f; 298 denom->issue.fees.refund = f; 299 denom->issue.denom_hash = denom->h_denom_pub; 300 TDB_fill (&denom->issue.signature, 301 sizeof (denom->issue.signature), 302 seed); 303 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 304 TALER_EXCHANGEDB_insert_denomination_info (pg, 305 &denom->pub, 306 &denom->issue)); 307 { 308 struct GNUNET_PQ_QueryParam params[] = { 309 GNUNET_PQ_query_param_auto_from_type (&denom->h_denom_pub), 310 GNUNET_PQ_query_param_end 311 }; 312 struct GNUNET_PQ_ResultSpec rs[] = { 313 GNUNET_PQ_result_spec_uint64 ("denominations_serial", 314 &denom->serial), 315 GNUNET_PQ_result_spec_end 316 }; 317 318 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 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 TDB_denom_free (struct TDB_Denom *denom) 336 { 337 TALER_denom_pub_free (&denom->pub); 338 TALER_denom_priv_free (&denom->priv); 339 } 340 341 342 void 343 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 memset (coin, 355 0, 356 sizeof (*coin)); 357 TDB_FILL (coin->coin_pub, 358 seed); 359 coin->denom_pub_hash = denom->h_denom_pub; 360 coin->no_age_commitment = true; 361 TDB_denom_sig (seed, 362 &coin->denom_sig); 363 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 GNUNET_assert ( (TALER_EXCHANGEDB_CKS_ADDED == cks) || 371 (TALER_EXCHANGEDB_CKS_PRESENT == cks) ); 372 if (NULL != known_coin_id) 373 *known_coin_id = kci; 374 } 375 376 377 void 378 TDB_coin_free (struct TALER_CoinPublicInfo *coin) 379 { 380 TALER_denom_sig_free (&coin->denom_sig); 381 } 382 383 384 void 385 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 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 TALER_full_payto_hash (account->payto, 398 &account->h_full); 399 npayto = TALER_payto_normalize (account->payto); 400 GNUNET_assert (NULL != npayto.normalized_payto); 401 TALER_normalized_payto_hash (npayto, 402 &account->h_normalized); 403 GNUNET_free (npayto.normalized_payto); 404 fhex = TDB_hex (&account->h_full, 405 sizeof (account->h_full)); 406 nhex = TDB_hex (&account->h_normalized, 407 sizeof (account->h_normalized)); 408 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 GNUNET_free (fhex); 425 GNUNET_free (nhex); 426 } 427 428 429 void 430 TDB_account_free (struct TDB_Account *account) 431 { 432 GNUNET_free (account->payto.full_payto); 433 } 434 435 436 void 437 TDB_reserve (struct TALER_EXCHANGEDB_PostgresContext *pg, 438 uint32_t seed, 439 const char *balance, 440 struct TALER_ReservePublicKeyP *reserve_pub) 441 { 442 struct TALER_Amount b = TDB_amount (balance); 443 char *hex; 444 445 TDB_fill (reserve_pub, 446 sizeof (*reserve_pub), 447 seed); 448 hex = TDB_hex (reserve_pub, 449 sizeof (*reserve_pub)); 450 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 GNUNET_free (hex); 465 } 466 467 468 void 469 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 struct TALER_Amount purse_fee = TDB_amount ("0"); 476 enum GNUNET_DB_QueryStatus qs; 477 bool in_conflict = false; 478 479 memset (purse, 480 0, 481 sizeof (*purse)); 482 TDB_fill (&purse->purse_pub, 483 sizeof (purse->purse_pub), 484 seed); 485 TDB_fill (&purse->merge_pub, 486 sizeof (purse->merge_pub), 487 seed); 488 TDB_fill (&purse->h_contract_terms, 489 sizeof (purse->h_contract_terms), 490 seed); 491 TDB_fill (&purse->purse_sig, 492 sizeof (purse->purse_sig), 493 seed); 494 purse->purse_expiration = purse_expiration; 495 purse->amount = TDB_amount (amount); 496 qs = TALER_EXCHANGEDB_insert_purse_request ( 497 pg, 498 &purse->purse_pub, 499 &purse->merge_pub, 500 purse_expiration, 501 &purse->h_contract_terms, 502 0, 503 TALER_WAMF_MODE_CREATE_FROM_PURSE_QUOTA, 504 &purse_fee, 505 &purse->amount, 506 &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 GNUNET_assert (0 <= qs); 511 GNUNET_assert (! in_conflict); 512 } 513 514 515 void 516 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 memset (dep, 535 0, 536 sizeof (*dep)); 537 TDB_fill (&dep->merchant_pub, 538 sizeof (dep->merchant_pub), 539 seed); 540 TDB_fill (&dep->merchant_sig, 541 sizeof (dep->merchant_sig), 542 seed); 543 TDB_fill (&dep->h_contract_terms, 544 sizeof (dep->h_contract_terms), 545 seed); 546 TDB_fill (&dep->wire_salt, 547 sizeof (dep->wire_salt), 548 seed); 549 TALER_merchant_wire_signature_hash (account->payto, 550 &dep->wire_salt, 551 &dep->h_wire); 552 dep->wire_deadline = wire_deadline; 553 dep->refund_deadline = refund_deadline; 554 memset (&cdi, 555 0, 556 sizeof (cdi)); 557 cdi.coin = *coin; 558 TDB_fill (&cdi.csig, 559 sizeof (cdi.csig), 560 seed); 561 cdi.amount_with_fee = TDB_amount (amount); 562 fees[0] = TDB_amount (fee); 563 memset (&bd, 564 0, 565 sizeof (bd)); 566 bd.merchant_pub = dep->merchant_pub; 567 bd.merchant_sig = dep->merchant_sig; 568 bd.h_contract_terms = dep->h_contract_terms; 569 bd.no_wallet_data_hash = true; 570 bd.wire_target_h_payto = account->h_full; 571 bd.wire_salt = dep->wire_salt; 572 bd.wallet_timestamp = wire_deadline; 573 bd.refund_deadline = refund_deadline; 574 bd.wire_deadline = wire_deadline; 575 bd.receiver_wire_account = account->payto; 576 bd.cdis = &cdi; 577 bd.num_cdis = 1; 578 dep->exchange_timestamp = GNUNET_TIME_timestamp_get (); 579 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 GNUNET_assert (balance_ok); 589 GNUNET_assert (! in_conflict); 590 { 591 struct GNUNET_PQ_QueryParam params[] = { 592 GNUNET_PQ_query_param_auto_from_type (&dep->merchant_pub), 593 GNUNET_PQ_query_param_auto_from_type (&dep->h_contract_terms), 594 GNUNET_PQ_query_param_end 595 }; 596 struct GNUNET_PQ_ResultSpec rs[] = { 597 GNUNET_PQ_result_spec_uint64 ("batch_deposit_serial_id", 598 &dep->serial), 599 GNUNET_PQ_result_spec_end 600 }; 601 602 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 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 609 GNUNET_PQ_eval_prepared_singleton_select (pg->conn, 610 "", 611 params, 612 rs)); 613 } 614 } 615 616 617 uint64_t 618 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 struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); 626 struct TALER_Amount reserve_balance; 627 struct TALER_BlindedDenominationSignature denom_sig; 628 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 memset (&wd, 639 0, 640 sizeof (wd)); 641 wd.amount_with_fee = TDB_amount (amount); 642 wd.reserve_pub = *reserve_pub; 643 TDB_fill (&wd.planchets_h, 644 sizeof (wd.planchets_h), 645 seed); 646 TDB_fill (&wd.reserve_sig, 647 sizeof (wd.reserve_sig), 648 seed); 649 wd.num_coins = 1; 650 wd.denom_serials = &denom_serial; 651 TDB_blinded_denom_sig (seed, 652 &denom_sig); 653 wd.denom_sigs = &denom_sig; 654 wd.no_blinding_seed = true; 655 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 GNUNET_assert (balance_ok); 668 GNUNET_assert (age_ok); 669 GNUNET_assert (! nonce_reuse); 670 TALER_blinded_denom_sig_free (&denom_sig); 671 { 672 struct GNUNET_PQ_QueryParam params[] = { 673 GNUNET_PQ_query_param_auto_from_type (&wd.planchets_h), 674 GNUNET_PQ_query_param_end 675 }; 676 struct GNUNET_PQ_ResultSpec rs[] = { 677 GNUNET_PQ_result_spec_uint64 ("withdraw_id", 678 &withdraw_id), 679 GNUNET_PQ_result_spec_end 680 }; 681 682 GNUNET_assert (GNUNET_OK == 683 GNUNET_PQ_prepare_anon (pg->conn, 684 "SELECT withdraw_id" 685 " FROM withdraw" 686 " WHERE planchets_h=$1;")); 687 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 688 GNUNET_PQ_eval_prepared_singleton_select (pg->conn, 689 "", 690 params, 691 rs)); 692 } 693 return withdraw_id; 694 } 695 696 697 uint64_t 698 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 struct TALER_Amount a = TDB_amount (amount); 705 uint64_t serial; 706 char *rhex; 707 char *ahex; 708 709 TDB_reserve (pg, 710 seed, 711 amount, 712 reserve_pub); 713 rhex = TDB_hex (reserve_pub, 714 sizeof (*reserve_pub)); 715 ahex = TDB_hex (&account->h_full, 716 sizeof (account->h_full)); 717 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 struct GNUNET_PQ_QueryParam params[] = { 735 GNUNET_PQ_query_param_auto_from_type (reserve_pub), 736 GNUNET_PQ_query_param_end 737 }; 738 struct GNUNET_PQ_ResultSpec rs[] = { 739 GNUNET_PQ_result_spec_uint64 ("reserve_in_serial_id", 740 &serial), 741 GNUNET_PQ_result_spec_end 742 }; 743 744 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 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 GNUNET_free (rhex); 758 GNUNET_free (ahex); 759 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 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 unsigned int ran = 0; 779 780 (void) cls; 781 (void) args; 782 (void) cfgfile; 783 pg = TALER_EXCHANGEDB_connect_admin (cfg); 784 if (NULL == pg) 785 { 786 fprintf (stderr, 787 "Failed to connect to the database\n"); 788 result = 77; 789 return; 790 } 791 if (GNUNET_OK != 792 TALER_EXCHANGEDB_create_tables (pg, 793 false, 794 0)) 795 { 796 fprintf (stderr, 797 "Failed to create the database schema\n"); 798 result = 77; 799 goto cleanup; 800 } 801 for (unsigned int i = 0; NULL != tdb_tests[i].name; i++) 802 { 803 if ( (NULL != only) && 804 (0 != strcmp (only, 805 tdb_tests[i].name)) ) 806 continue; 807 fprintf (stderr, 808 "Running check `%s'\n", 809 tdb_tests[i].name); 810 ran++; 811 if (0 != tdb_tests[i].fn (pg)) 812 { 813 fprintf (stderr, 814 "Check `%s' FAILED\n", 815 tdb_tests[i].name); 816 result = 1; 817 } 818 } 819 if (0 == ran) 820 { 821 fprintf (stderr, 822 "No check matched `%s'\n", 823 only); 824 result = 1; 825 } 826 cleanup: 827 TALER_EXCHANGEDB_disconnect (pg); 828 } 829 830 831 int 832 TDB_main (int argc, 833 char *const *argv, 834 const char *binary, 835 const char *description, 836 const struct TDB_Test *tests) 837 { 838 struct GNUNET_GETOPT_CommandLineOption options[] = { 839 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 result = 0; 849 tdb_tests = tests; 850 ret = GNUNET_PROGRAM_run (TALER_EXCHANGE_project_data (), 851 argc, 852 argv, 853 binary, 854 description, 855 options, 856 &run, 857 NULL); 858 if (GNUNET_SYSERR == ret) 859 return 3; 860 if (GNUNET_NO == ret) 861 return 0; 862 return result; 863 } 864 865 866 /* end of test_common.c */