exchange

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

perf_select_refunds_by_coin.c (20542B)


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