test_common.c (27186B)
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 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_EXCHANGEDB_CoinKnownResult res; 350 const struct TALER_CoinPublicInfo *coins[1] = { coin }; 351 352 memset (coin, 353 0, 354 sizeof (*coin)); 355 TDB_FILL (coin->coin_pub, 356 seed); 357 coin->denom_pub_hash = denom->h_denom_pub; 358 coin->no_age_commitment = true; 359 TDB_denom_sig (seed, 360 &coin->denom_sig); 361 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 GNUNET_assert ( (TALER_EXCHANGEDB_CKS_ADDED == res.status) || 369 (TALER_EXCHANGEDB_CKS_PRESENT == res.status) ); 370 if (NULL != known_coin_id) 371 *known_coin_id = res.known_coin_id; 372 } 373 374 375 void 376 TDB_coin_free (struct TALER_CoinPublicInfo *coin) 377 { 378 TALER_denom_sig_free (&coin->denom_sig); 379 } 380 381 382 void 383 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 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 TALER_full_payto_hash (account->payto, 396 &account->h_full); 397 npayto = TALER_payto_normalize (account->payto); 398 GNUNET_assert (NULL != npayto.normalized_payto); 399 TALER_normalized_payto_hash (npayto, 400 &account->h_normalized); 401 GNUNET_free (npayto.normalized_payto); 402 fhex = TDB_hex (&account->h_full, 403 sizeof (account->h_full)); 404 nhex = TDB_hex (&account->h_normalized, 405 sizeof (account->h_normalized)); 406 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 GNUNET_free (fhex); 423 GNUNET_free (nhex); 424 } 425 426 427 void 428 TDB_account_free (struct TDB_Account *account) 429 { 430 GNUNET_free (account->payto.full_payto); 431 } 432 433 434 void 435 TDB_reserve (struct TALER_EXCHANGEDB_PostgresContext *pg, 436 uint32_t seed, 437 const char *balance, 438 struct TALER_ReservePublicKeyP *reserve_pub) 439 { 440 struct TALER_Amount b = TDB_amount (balance); 441 char *hex; 442 443 TDB_fill (reserve_pub, 444 sizeof (*reserve_pub), 445 seed); 446 hex = TDB_hex (reserve_pub, 447 sizeof (*reserve_pub)); 448 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 GNUNET_free (hex); 463 } 464 465 466 void 467 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 struct TALER_Amount purse_fee = TDB_amount ("0"); 474 enum GNUNET_DB_QueryStatus qs; 475 bool in_conflict = false; 476 477 memset (purse, 478 0, 479 sizeof (*purse)); 480 TDB_fill (&purse->purse_pub, 481 sizeof (purse->purse_pub), 482 seed); 483 TDB_fill (&purse->merge_pub, 484 sizeof (purse->merge_pub), 485 seed); 486 TDB_fill (&purse->h_contract_terms, 487 sizeof (purse->h_contract_terms), 488 seed); 489 TDB_fill (&purse->purse_sig, 490 sizeof (purse->purse_sig), 491 seed); 492 purse->purse_expiration = purse_expiration; 493 purse->amount = TDB_amount (amount); 494 qs = TALER_EXCHANGEDB_insert_purse_request ( 495 pg, 496 &purse->purse_pub, 497 &purse->merge_pub, 498 purse_expiration, 499 &purse->h_contract_terms, 500 0, 501 TALER_WAMF_MODE_CREATE_FROM_PURSE_QUOTA, 502 &purse_fee, 503 &purse->amount, 504 &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 GNUNET_assert (0 <= qs); 509 GNUNET_assert (! in_conflict); 510 } 511 512 513 void 514 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 memset (dep, 533 0, 534 sizeof (*dep)); 535 TDB_fill (&dep->merchant_pub, 536 sizeof (dep->merchant_pub), 537 seed); 538 TDB_fill (&dep->merchant_sig, 539 sizeof (dep->merchant_sig), 540 seed); 541 TDB_fill (&dep->h_contract_terms, 542 sizeof (dep->h_contract_terms), 543 seed); 544 TDB_fill (&dep->wire_salt, 545 sizeof (dep->wire_salt), 546 seed); 547 TALER_merchant_wire_signature_hash (account->payto, 548 &dep->wire_salt, 549 &dep->h_wire); 550 dep->wire_deadline = wire_deadline; 551 dep->refund_deadline = refund_deadline; 552 memset (&cdi, 553 0, 554 sizeof (cdi)); 555 cdi.coin = *coin; 556 TDB_fill (&cdi.csig, 557 sizeof (cdi.csig), 558 seed); 559 cdi.amount_with_fee = TDB_amount (amount); 560 fees[0] = TDB_amount (fee); 561 memset (&bd, 562 0, 563 sizeof (bd)); 564 bd.merchant_pub = dep->merchant_pub; 565 bd.merchant_sig = dep->merchant_sig; 566 bd.h_contract_terms = dep->h_contract_terms; 567 bd.no_wallet_data_hash = true; 568 bd.wire_target_h_payto = account->h_full; 569 bd.wire_salt = dep->wire_salt; 570 bd.wallet_timestamp = wire_deadline; 571 bd.refund_deadline = refund_deadline; 572 bd.wire_deadline = wire_deadline; 573 bd.receiver_wire_account = account->payto; 574 bd.cdis = &cdi; 575 bd.num_cdis = 1; 576 dep->exchange_timestamp = GNUNET_TIME_timestamp_get (); 577 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 GNUNET_assert (balance_ok); 587 GNUNET_assert (! in_conflict); 588 { 589 struct GNUNET_PQ_QueryParam params[] = { 590 GNUNET_PQ_query_param_auto_from_type (&dep->merchant_pub), 591 GNUNET_PQ_query_param_auto_from_type (&dep->h_contract_terms), 592 GNUNET_PQ_query_param_end 593 }; 594 struct GNUNET_PQ_ResultSpec rs[] = { 595 GNUNET_PQ_result_spec_uint64 ("batch_deposit_serial_id", 596 &dep->serial), 597 GNUNET_PQ_result_spec_end 598 }; 599 600 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 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 607 GNUNET_PQ_eval_prepared_singleton_select (pg->conn, 608 "", 609 params, 610 rs)); 611 } 612 } 613 614 615 uint64_t 616 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 struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); 624 struct TALER_Amount reserve_balance; 625 struct TALER_BlindedDenominationSignature denom_sig; 626 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 memset (&wd, 637 0, 638 sizeof (wd)); 639 wd.amount_with_fee = TDB_amount (amount); 640 wd.reserve_pub = *reserve_pub; 641 TDB_fill (&wd.planchets_h, 642 sizeof (wd.planchets_h), 643 seed); 644 TDB_fill (&wd.reserve_sig, 645 sizeof (wd.reserve_sig), 646 seed); 647 wd.num_coins = 1; 648 wd.denom_serials = &denom_serial; 649 TDB_blinded_denom_sig (seed, 650 &denom_sig); 651 wd.denom_sigs = &denom_sig; 652 wd.no_blinding_seed = true; 653 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 GNUNET_assert (balance_ok); 666 GNUNET_assert (age_ok); 667 GNUNET_assert (! nonce_reuse); 668 TALER_blinded_denom_sig_free (&denom_sig); 669 { 670 struct GNUNET_PQ_QueryParam params[] = { 671 GNUNET_PQ_query_param_auto_from_type (&wd.planchets_h), 672 GNUNET_PQ_query_param_end 673 }; 674 struct GNUNET_PQ_ResultSpec rs[] = { 675 GNUNET_PQ_result_spec_uint64 ("withdraw_id", 676 &withdraw_id), 677 GNUNET_PQ_result_spec_end 678 }; 679 680 GNUNET_assert (GNUNET_OK == 681 GNUNET_PQ_prepare_anon (pg->conn, 682 "SELECT withdraw_id" 683 " FROM withdraw" 684 " WHERE planchets_h=$1;")); 685 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 686 GNUNET_PQ_eval_prepared_singleton_select (pg->conn, 687 "", 688 params, 689 rs)); 690 } 691 return withdraw_id; 692 } 693 694 695 uint64_t 696 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 struct TALER_Amount a = TDB_amount (amount); 703 uint64_t serial; 704 char *rhex; 705 char *ahex; 706 707 TDB_reserve (pg, 708 seed, 709 amount, 710 reserve_pub); 711 rhex = TDB_hex (reserve_pub, 712 sizeof (*reserve_pub)); 713 ahex = TDB_hex (&account->h_full, 714 sizeof (account->h_full)); 715 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 struct GNUNET_PQ_QueryParam params[] = { 733 GNUNET_PQ_query_param_auto_from_type (reserve_pub), 734 GNUNET_PQ_query_param_end 735 }; 736 struct GNUNET_PQ_ResultSpec rs[] = { 737 GNUNET_PQ_result_spec_uint64 ("reserve_in_serial_id", 738 &serial), 739 GNUNET_PQ_result_spec_end 740 }; 741 742 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 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 GNUNET_free (rhex); 756 GNUNET_free (ahex); 757 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 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 unsigned int ran = 0; 777 778 (void) cls; 779 (void) args; 780 (void) cfgfile; 781 pg = TALER_EXCHANGEDB_connect_admin (cfg); 782 if (NULL == pg) 783 { 784 fprintf (stderr, 785 "Failed to connect to the database\n"); 786 result = 77; 787 return; 788 } 789 if (GNUNET_OK != 790 TALER_EXCHANGEDB_create_tables (pg, 791 false, 792 0)) 793 { 794 fprintf (stderr, 795 "Failed to create the database schema\n"); 796 result = 77; 797 goto cleanup; 798 } 799 for (unsigned int i = 0; NULL != tdb_tests[i].name; i++) 800 { 801 if ( (NULL != only) && 802 (0 != strcmp (only, 803 tdb_tests[i].name)) ) 804 continue; 805 fprintf (stderr, 806 "Running check `%s'\n", 807 tdb_tests[i].name); 808 ran++; 809 if (0 != tdb_tests[i].fn (pg)) 810 { 811 fprintf (stderr, 812 "Check `%s' FAILED\n", 813 tdb_tests[i].name); 814 result = 1; 815 } 816 } 817 if (0 == ran) 818 { 819 fprintf (stderr, 820 "No check matched `%s'\n", 821 only); 822 result = 1; 823 } 824 cleanup: 825 TALER_EXCHANGEDB_disconnect (pg); 826 } 827 828 829 int 830 TDB_main (int argc, 831 char *const *argv, 832 const char *binary, 833 const char *description, 834 const struct TDB_Test *tests) 835 { 836 struct GNUNET_GETOPT_CommandLineOption options[] = { 837 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 result = 0; 847 tdb_tests = tests; 848 ret = GNUNET_PROGRAM_run (TALER_EXCHANGE_project_data (), 849 argc, 850 argv, 851 binary, 852 description, 853 options, 854 &run, 855 NULL); 856 if (GNUNET_SYSERR == ret) 857 return 3; 858 if (GNUNET_NO == ret) 859 return 0; 860 return result; 861 } 862 863 864 /* end of test_common.c */