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