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 (20953B)


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