perf_deposits_get_ready.c (18868B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2024 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/perf_deposits_get_ready.c 18 * @brief benchmark for deposits_get_ready 19 * @author Joseph Xu 20 */ 21 #include "taler/platform.h" 22 #include "taler/taler_exchangedb_lib.h" 23 #include "taler/taler_json_lib.h" 24 #include "taler/taler_exchangedb_plugin.h" 25 #include "math.h" 26 27 /** 28 * Global result from the testcase. 29 */ 30 static int result; 31 32 /** 33 * Report line of error if @a cond is true, and jump to label "drop". 34 */ 35 #define FAILIF(cond) \ 36 do { \ 37 if (! (cond)) {break;} \ 38 GNUNET_break (0); \ 39 goto drop; \ 40 } while (0) 41 42 43 /** 44 * Initializes @a ptr with random data. 45 */ 46 #define RND_BLK(ptr) \ 47 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, \ 48 sizeof (*ptr)) 49 50 /** 51 * Initializes @a ptr with zeros. 52 */ 53 #define ZR_BLK(ptr) \ 54 memset (ptr, 0, sizeof (*ptr)) 55 56 /** 57 * Currency we use. Must match test-exchange-db-*.conf. 58 */ 59 #define CURRENCY "EUR" 60 #define RSA_KEY_SIZE 1024 61 #define NUM_ROWS 1000 62 #define ROUNDS 100 63 #define MELT_NEW_COINS 5 64 #define MELT_NOREVEAL_INDEX 1 65 66 /** 67 * Database plugin under test. 68 */ 69 static struct TALER_EXCHANGEDB_Plugin *plugin; 70 71 static struct TALER_MerchantWireHashP h_wire_wt; 72 73 /** 74 * Denomination keys used for fresh coins in melt test. 75 */ 76 static struct DenomKeyPair **new_dkp; 77 78 static struct CoinInfo *revealed_coins; 79 80 struct DenomKeyPair 81 { 82 struct TALER_DenominationPrivateKey priv; 83 struct TALER_DenominationPublicKey pub; 84 }; 85 86 87 struct CoinInfo 88 { 89 struct TALER_DenominationHashP h_denom_pub; 90 struct TALER_CoinSpendSignatureP orig_coin_link_sig; 91 struct TALER_BlindedCoinHashP coin_envelope_hash; 92 struct TALER_BlindedDenominationSignature coin_sig; 93 struct TALER_ExchangeBlindingValues exchange_vals; 94 struct TALER_BlindedPlanchet blinded_planchet; 95 }; 96 97 /** 98 * Destroy a denomination key pair. The key is not necessarily removed from the DB. 99 * 100 * @param dkp the key pair to destroy 101 */ 102 static void 103 destroy_denom_key_pair (struct DenomKeyPair *dkp) 104 { 105 TALER_denom_pub_free (&dkp->pub); 106 TALER_denom_priv_free (&dkp->priv); 107 GNUNET_free (dkp); 108 } 109 110 111 /** 112 * Create a denomination key pair by registering the denomination in the DB. 113 * 114 * @param size the size of the denomination key 115 * @param now time to use for key generation, legal expiration will be 3h later. 116 * @param fees fees to use 117 * @return the denominaiton key pair; NULL upon error 118 */ 119 static struct DenomKeyPair * 120 create_denom_key_pair (unsigned int size, 121 struct GNUNET_TIME_Timestamp now, 122 const struct TALER_Amount *value, 123 const struct TALER_DenomFeeSet *fees) 124 { 125 struct DenomKeyPair *dkp; 126 struct TALER_EXCHANGEDB_DenominationKey dki; 127 struct TALER_EXCHANGEDB_DenominationKeyInformation issue2; 128 129 dkp = GNUNET_new (struct DenomKeyPair); 130 GNUNET_assert (GNUNET_OK == 131 TALER_denom_priv_create (&dkp->priv, 132 &dkp->pub, 133 GNUNET_CRYPTO_BSA_RSA, 134 size)); 135 memset (&dki, 136 0, 137 sizeof (struct TALER_EXCHANGEDB_DenominationKey)); 138 dki.denom_pub = dkp->pub; 139 dki.issue.start = now; 140 dki.issue.expire_withdraw 141 = GNUNET_TIME_absolute_to_timestamp ( 142 GNUNET_TIME_absolute_add ( 143 now.abs_time, 144 GNUNET_TIME_UNIT_HOURS)); 145 dki.issue.expire_deposit 146 = GNUNET_TIME_absolute_to_timestamp ( 147 GNUNET_TIME_absolute_add ( 148 now.abs_time, 149 GNUNET_TIME_relative_multiply ( 150 GNUNET_TIME_UNIT_HOURS, 2))); 151 dki.issue.expire_legal 152 = GNUNET_TIME_absolute_to_timestamp ( 153 GNUNET_TIME_absolute_add ( 154 now.abs_time, 155 GNUNET_TIME_relative_multiply ( 156 GNUNET_TIME_UNIT_HOURS, 3))); 157 dki.issue.value = *value; 158 dki.issue.fees = *fees; 159 TALER_denom_pub_hash (&dkp->pub, 160 &dki.issue.denom_hash); 161 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 162 plugin->insert_denomination_info (plugin->cls, 163 &dki.denom_pub, 164 &dki.issue)) 165 { 166 GNUNET_break (0); 167 destroy_denom_key_pair (dkp); 168 return NULL; 169 } 170 memset (&issue2, 0, sizeof (issue2)); 171 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 172 plugin->get_denomination_info (plugin->cls, 173 &dki.issue.denom_hash, 174 NULL, 175 &issue2)) 176 { 177 GNUNET_break (0); 178 destroy_denom_key_pair (dkp); 179 return NULL; 180 } 181 if (0 != GNUNET_memcmp (&dki.issue, 182 &issue2)) 183 { 184 GNUNET_break (0); 185 destroy_denom_key_pair (dkp); 186 return NULL; 187 } 188 return dkp; 189 } 190 191 192 /** 193 * Main function that will be run by the scheduler. 194 * 195 * @param cls closure with config 196 */ 197 198 static void 199 run (void *cls) 200 { 201 struct TALER_EXCHANGEDB_Refresh refresh; 202 struct GNUNET_CONFIGURATION_Handle *cfg = cls; 203 const uint32_t num_partitions = 10; 204 struct TALER_Amount value; 205 struct TALER_EXCHANGEDB_CollectableBlindcoin cbc; 206 struct TALER_DenominationPublicKey *new_denom_pubs = NULL; 207 struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO; 208 unsigned long long sqrs = 0; 209 struct TALER_EXCHANGEDB_CoinDepositInformation *depos; 210 struct TALER_EXCHANGEDB_BatchDeposit bd; 211 struct TALER_EXCHANGEDB_Refund *ref; 212 unsigned int *perm; 213 unsigned long long duration_sq; 214 struct CoinInfo *ccoin; 215 struct TALER_DenomFeeSet fees; 216 struct GNUNET_CRYPTO_BlindingInputValues bi = { 217 .cipher = GNUNET_CRYPTO_BSA_RSA, 218 .rc = 0 219 }; 220 struct TALER_ExchangeBlindingValues alg_values = { 221 .blinding_inputs = &bi 222 }; 223 224 ref = GNUNET_new_array (ROUNDS + 1, 225 struct TALER_EXCHANGEDB_Refund); 226 depos = GNUNET_new_array (ROUNDS + 1, 227 struct TALER_EXCHANGEDB_CoinDepositInformation); 228 229 if (NULL == 230 (plugin = TALER_EXCHANGEDB_plugin_load (cfg, 231 true))) 232 { 233 GNUNET_break (0); 234 result = 77; 235 return; 236 } 237 (void) plugin->drop_tables (plugin->cls); 238 if (GNUNET_OK != 239 plugin->create_tables (plugin->cls, 240 true, 241 num_partitions)) 242 { 243 GNUNET_break (0); 244 result = 77; 245 goto cleanup; 246 } 247 if (GNUNET_OK != 248 plugin->preflight (plugin->cls)) 249 { 250 GNUNET_break (0); 251 goto cleanup; 252 } 253 254 GNUNET_assert (GNUNET_OK == 255 TALER_string_to_amount (CURRENCY ":1.000010", 256 &value)); 257 GNUNET_assert (GNUNET_OK == 258 TALER_string_to_amount (CURRENCY ":0.000010", 259 &fees.withdraw)); 260 GNUNET_assert (GNUNET_OK == 261 TALER_string_to_amount (CURRENCY ":0.000010", 262 &fees.deposit)); 263 GNUNET_assert (GNUNET_OK == 264 TALER_string_to_amount (CURRENCY ":0.000010", 265 &fees.refresh)); 266 GNUNET_assert (GNUNET_OK == 267 TALER_string_to_amount (CURRENCY ":0.000010", 268 &fees.refund)); 269 { 270 ZR_BLK (&cbc); 271 new_dkp = GNUNET_new_array (MELT_NEW_COINS, 272 struct DenomKeyPair *); 273 new_denom_pubs = GNUNET_new_array (MELT_NEW_COINS, 274 struct TALER_DenominationPublicKey); 275 revealed_coins 276 = GNUNET_new_array (MELT_NEW_COINS, 277 struct CoinInfo); 278 for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++) 279 { 280 struct GNUNET_TIME_Timestamp now; 281 struct GNUNET_CRYPTO_RsaBlindedMessage *rp; 282 struct TALER_BlindedPlanchet *bp; 283 284 now = GNUNET_TIME_timestamp_get (); 285 new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE, 286 now, 287 &value, 288 &fees); 289 GNUNET_assert (NULL != new_dkp[cnt]); 290 new_denom_pubs[cnt] = new_dkp[cnt]->pub; 291 ccoin = &revealed_coins[cnt]; 292 bp = &ccoin->blinded_planchet; 293 bp->blinded_message = GNUNET_new (struct GNUNET_CRYPTO_BlindedMessage); 294 bp->blinded_message->cipher = GNUNET_CRYPTO_BSA_RSA; 295 bp->blinded_message->rc = 1; 296 rp = &bp->blinded_message->details.rsa_blinded_message; 297 rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 ( 298 GNUNET_CRYPTO_QUALITY_WEAK, 299 (RSA_KEY_SIZE / 8) - 1); 300 rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size); 301 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, 302 rp->blinded_msg, 303 rp->blinded_msg_size); 304 TALER_denom_pub_hash (&new_dkp[cnt]->pub, 305 &ccoin->h_denom_pub); 306 ccoin->exchange_vals = alg_values; 307 TALER_coin_ev_hash (bp, 308 &ccoin->h_denom_pub, 309 &ccoin->coin_envelope_hash); 310 GNUNET_assert (GNUNET_OK == 311 TALER_denom_sign_blinded (&ccoin->coin_sig, 312 &new_dkp[cnt]->priv, 313 true, 314 bp)); 315 TALER_coin_ev_hash (bp, 316 &cbc.denom_pub_hash, 317 &cbc.h_coin_envelope); 318 GNUNET_assert ( 319 GNUNET_OK == 320 TALER_denom_sign_blinded ( 321 &cbc.sig, 322 &new_dkp[cnt]->priv, 323 false, 324 bp)); 325 } 326 } 327 perm = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_NONCE, 328 NUM_ROWS); 329 FAILIF (GNUNET_OK != 330 plugin->start (plugin->cls, 331 "Transaction")); 332 for (unsigned int j = 0; j < NUM_ROWS; j++) 333 { 334 /*** NEED TO INSERT REFRESH COMMITMENTS + ENSURECOIN ***/ 335 union GNUNET_CRYPTO_BlindingSecretP bks; 336 struct GNUNET_TIME_Timestamp deadline; 337 struct TALER_CoinSpendPublicKeyP coin_pub; 338 struct TALER_ReservePublicKeyP reserve_pub; 339 struct TALER_CoinPubHashP c_hash; 340 unsigned int k = (unsigned int) rand () % 5; 341 unsigned int i = perm[j]; 342 343 if (i >= ROUNDS) 344 i = ROUNDS; /* throw-away slot, do not keep around */ 345 RND_BLK (&coin_pub); 346 RND_BLK (&c_hash); 347 RND_BLK (&reserve_pub); 348 RND_BLK (&cbc.reserve_sig); 349 TALER_denom_pub_hash (&new_dkp[k]->pub, 350 &cbc.denom_pub_hash); 351 deadline = GNUNET_TIME_timestamp_get (); 352 depos[i].coin.coin_pub = coin_pub; 353 TALER_denom_pub_hash (&new_dkp[k]->pub, 354 &depos[i].coin.denom_pub_hash); 355 GNUNET_assert (GNUNET_OK == 356 TALER_denom_sig_unblind (&depos[i].coin.denom_sig, 357 &ccoin->coin_sig, 358 &bks, 359 &c_hash, 360 &alg_values, 361 &new_dkp[k]->pub)); 362 RND_BLK (&bd.merchant_pub); 363 RND_BLK (&depos[i].csig); 364 RND_BLK (&bd.h_contract_terms); 365 RND_BLK (&bd.wire_salt); 366 depos[i].amount_with_fee = value; 367 bd.refund_deadline = deadline; 368 bd.wire_deadline = deadline; 369 bd.receiver_wire_account.full_payto = 370 (char *) "payto://iban/DE67830654080004822650?receiver-name=Test"; 371 TALER_merchant_wire_signature_hash ( 372 bd.receiver_wire_account, 373 &bd.wire_salt, 374 &h_wire_wt); 375 bd.num_cdis = 1; 376 bd.cdis = &depos[i]; 377 cbc.reserve_pub = reserve_pub; 378 cbc.amount_with_fee = value; 379 GNUNET_assert (GNUNET_OK == 380 TALER_amount_set_zero (CURRENCY, 381 &cbc.withdraw_fee)); 382 { 383 bool nonce_reuse; 384 bool balance_ok; 385 bool age_ok; 386 bool idempotent; 387 uint16_t noreveal_index; 388 struct TALER_Amount reserve_balance; 389 uint16_t allowed_minimum_age; 390 uint32_t reserve_birthday; 391 struct GNUNET_TIME_Timestamp now; 392 struct TALER_EXCHANGEDB_Withdraw withdraw = { 393 .amount_with_fee = value, 394 .reserve_pub = reserve_pub, 395 }; 396 397 now = GNUNET_TIME_timestamp_get (); 398 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 399 plugin->do_withdraw (plugin->cls, 400 &withdraw, 401 &now, 402 &balance_ok, 403 &reserve_balance, 404 &age_ok, 405 &allowed_minimum_age, 406 &reserve_birthday, 407 &idempotent, 408 &noreveal_index, 409 &nonce_reuse)); 410 } 411 { 412 /* ENSURE_COIN_KNOWN */ 413 uint64_t known_coin_id; 414 struct TALER_DenominationHashP dph; 415 struct TALER_AgeCommitmentHashP agh; 416 FAILIF (TALER_EXCHANGEDB_CKS_ADDED != 417 plugin->ensure_coin_known (plugin->cls, 418 &depos[i].coin, 419 &known_coin_id, 420 &dph, 421 &agh)); 422 refresh.coin = depos[i].coin; 423 RND_BLK (&refresh.coin_sig); 424 RND_BLK (&refresh.rc); 425 refresh.amount_with_fee = value; 426 refresh.noreveal_index = MELT_NOREVEAL_INDEX; 427 } 428 { 429 struct GNUNET_TIME_Timestamp now; 430 struct TALER_Amount total; 431 bool balance_ok; 432 uint32_t bad_idx; 433 bool ctr_conflict; 434 435 GNUNET_assert (1 == bd.num_cdis); 436 now = GNUNET_TIME_timestamp_get (); 437 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 438 plugin->do_deposit (plugin->cls, 439 &bd, 440 &fees.deposit, 441 &now, 442 &total, 443 &balance_ok, 444 &bad_idx, 445 &ctr_conflict)); 446 } 447 if (ROUNDS == i) 448 TALER_denom_sig_free (&depos[i].coin.denom_sig); 449 } 450 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 451 plugin->commit (plugin->cls)); 452 GNUNET_free (perm); 453 /* End of benchmark setup */ 454 455 /**** CALL GET READY DEPOSIT ****/ 456 for (unsigned int r = 0; r< ROUNDS; r++) 457 { 458 struct GNUNET_TIME_Absolute time; 459 struct GNUNET_TIME_Relative duration; 460 struct TALER_MerchantPublicKeyP merchant_pub; 461 struct TALER_FullPayto payto_uri; 462 enum GNUNET_DB_QueryStatus qs; 463 char *metadata; 464 465 time = GNUNET_TIME_absolute_get (); 466 qs = plugin->get_ready_deposit (plugin->cls, 467 0, 468 INT32_MAX, 469 &merchant_pub, 470 &payto_uri, 471 &metadata); 472 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs); 473 duration = GNUNET_TIME_absolute_get_duration (time); 474 times = GNUNET_TIME_relative_add (times, 475 duration); 476 duration_sq = duration.rel_value_us * duration.rel_value_us; 477 GNUNET_assert (duration_sq / duration.rel_value_us == 478 duration.rel_value_us); 479 GNUNET_assert (sqrs + duration_sq >= sqrs); 480 sqrs += duration_sq; 481 GNUNET_free (payto_uri.full_payto); 482 GNUNET_free (metadata); 483 } 484 485 /* evaluation of performance */ 486 { 487 struct GNUNET_TIME_Relative avg; 488 double avg_dbl; 489 double variance; 490 491 avg = GNUNET_TIME_relative_divide (times, 492 ROUNDS); 493 avg_dbl = avg.rel_value_us; 494 variance = sqrs - (avg_dbl * avg_dbl * ROUNDS); 495 fprintf (stdout, 496 "%8llu ± %6.0f\n", 497 (unsigned long long) avg.rel_value_us, 498 sqrt (variance / (ROUNDS - 1))); 499 } 500 result = 0; 501 drop: 502 // GNUNET_break (GNUNET_OK == plugin->drop_tables (plugin->cls)); 503 cleanup: 504 if (NULL != revealed_coins) 505 { 506 for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++) 507 { 508 TALER_blinded_denom_sig_free (&revealed_coins[cnt].coin_sig); 509 TALER_blinded_planchet_free (&revealed_coins[cnt].blinded_planchet); 510 } 511 GNUNET_free (revealed_coins); 512 revealed_coins = NULL; 513 } 514 GNUNET_free (new_denom_pubs); 515 for (unsigned int cnt = 0; 516 (NULL != new_dkp) && (cnt < MELT_NEW_COINS) && (NULL != new_dkp[cnt]); 517 cnt++) 518 destroy_denom_key_pair (new_dkp[cnt]); 519 GNUNET_free (new_dkp); 520 for (unsigned int i = 0; i< ROUNDS; i++) 521 { 522 TALER_denom_sig_free (&depos[i].coin.denom_sig); 523 } 524 GNUNET_free (depos); 525 GNUNET_free (ref); 526 TALER_EXCHANGEDB_plugin_unload (plugin); 527 plugin = NULL; 528 } 529 530 531 int 532 main (int argc, 533 char *const argv[]) 534 { 535 const char *plugin_name; 536 char *config_filename; 537 struct GNUNET_CONFIGURATION_Handle *cfg; 538 539 (void) argc; 540 result = -1; 541 if (NULL == (plugin_name = strrchr (argv[0], (int) '-'))) 542 { 543 GNUNET_break (0); 544 return -1; 545 } 546 GNUNET_log_setup (argv[0], 547 "WARNING", 548 NULL); 549 plugin_name++; 550 { 551 char *testname; 552 553 GNUNET_asprintf (&testname, 554 "test-exchange-db-%s", 555 plugin_name); 556 GNUNET_asprintf (&config_filename, 557 "%s.conf", 558 testname); 559 GNUNET_free (testname); 560 } 561 cfg = GNUNET_CONFIGURATION_create (TALER_EXCHANGE_project_data ()); 562 if (GNUNET_OK != 563 GNUNET_CONFIGURATION_parse (cfg, 564 config_filename)) 565 { 566 GNUNET_break (0); 567 GNUNET_free (config_filename); 568 return 2; 569 } 570 GNUNET_SCHEDULER_run (&run, 571 cfg); 572 GNUNET_CONFIGURATION_destroy (cfg); 573 GNUNET_free (config_filename); 574 return result; 575 } 576 577 578 /* end of perf_deposits_get_ready.c */