exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

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 */