diff options
author | Christian Grothoff <christian@grothoff.org> | 2016-06-20 10:19:58 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2016-06-20 10:19:58 +0200 |
commit | 029911c757b4557064608ac0e3f27ad7f7a4b82f (patch) | |
tree | 2c5aea0df74ba33939a8b6832bcb8c9524018f1b /src | |
parent | 9fb17b3ca7b9360c1c32d45b180591a3220f9758 (diff) | |
parent | 17c8741e2049cde4099af3d1d132da3b48e427c5 (diff) |
Merge branch 'master' of git+ssh://git.taler.net/var/git/exchange
Diffstat (limited to 'src')
-rw-r--r-- | src/bank-lib/Makefile.am | 1 | ||||
-rw-r--r-- | src/benchmark/Makefile.am | 8 | ||||
-rw-r--r-- | src/benchmark/bank_details.json | 1 | ||||
-rw-r--r-- | src/benchmark/merchant_details.json | 2 | ||||
-rw-r--r-- | src/benchmark/sender_details.json | 1 | ||||
-rw-r--r-- | src/benchmark/taler-exchange-benchmark.c | 1643 | ||||
-rw-r--r-- | src/benchmark/taler-exchange-benchmark.conf | 92 | ||||
-rw-r--r-- | src/benchmark/test_benchmark_home/.config/taler/sepa.json | 9 | ||||
-rw-r--r-- | src/benchmark/test_benchmark_home/.config/taler/test.json | 8 | ||||
-rw-r--r-- | src/benchmark/test_benchmark_home/.local/share/taler/exchange/offline-keys/master.priv | 1 | ||||
-rw-r--r-- | src/exchange-lib/exchange_api_refresh.c | 35 | ||||
-rw-r--r-- | src/exchange-lib/exchange_api_refund.c | 3 | ||||
-rw-r--r-- | src/exchange-lib/test_exchange_api.conf | 2 | ||||
-rw-r--r-- | src/exchange-tools/taler-exchange-keyup.c | 6 |
14 files changed, 1224 insertions, 588 deletions
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am index 289135023..a87a2c467 100644 --- a/src/bank-lib/Makefile.am +++ b/src/bank-lib/Makefile.am @@ -38,6 +38,7 @@ libtalerfakebank_la_LIBADD = \ -lgnunetjson \ -lgnunetutil \ -ljansson \ + -lmicrohttpd \ $(XLIB) diff --git a/src/benchmark/Makefile.am b/src/benchmark/Makefile.am index e92a13944..f2c299b64 100644 --- a/src/benchmark/Makefile.am +++ b/src/benchmark/Makefile.am @@ -20,3 +20,11 @@ taler_exchange_benchmark_LDADD = \ -lgnunetcurl \ -lgnunetutil \ -ljansson + +EXTRA_DIST = \ + taler-exchange-benchmark.conf \ + bank-details.json \ + merchant-details.json \ + test_benchmark_home/.local/share/taler/exchange/offline-keys/master.priv \ + test_benchmark_home/.config/taler/test.json \ + test_benchmark_home/.config/taler/sepa.json diff --git a/src/benchmark/bank_details.json b/src/benchmark/bank_details.json new file mode 100644 index 000000000..bc46c48f3 --- /dev/null +++ b/src/benchmark/bank_details.json @@ -0,0 +1 @@ +{"type":"test", "bank_uri":"http://localhost:8082/", "account_number":63} diff --git a/src/benchmark/merchant_details.json b/src/benchmark/merchant_details.json index bda6e6cc0..c3869161d 100644 --- a/src/benchmark/merchant_details.json +++ b/src/benchmark/merchant_details.json @@ -1 +1 @@ -{"type":"test", "bank_uri":"https://bank.test.taler.net/", "account_number":64} +{"type":"test", "bank_uri":"http://localhost:8082/", "account_number":64} diff --git a/src/benchmark/sender_details.json b/src/benchmark/sender_details.json deleted file mode 100644 index d6f60005b..000000000 --- a/src/benchmark/sender_details.json +++ /dev/null @@ -1 +0,0 @@ -{"type":"test", "bank_uri":"https://bank.test.taler.net/", "account_number":63} diff --git a/src/benchmark/taler-exchange-benchmark.c b/src/benchmark/taler-exchange-benchmark.c index f2274e606..45ed28521 100644 --- a/src/benchmark/taler-exchange-benchmark.c +++ b/src/benchmark/taler-exchange-benchmark.c @@ -17,6 +17,7 @@ * @file src/benchmark/taler-exchange-benchmark.c * @brief exchange's benchmark * @author Marcello Stanisci + * @author Christian Grothoff */ #include "platform.h" #include "taler_util.h" @@ -28,72 +29,87 @@ #include <microhttpd.h> #include <jansson.h> -#define RUNXCG - /** - * How many coins the benchmark should operate on + * How much slack do we leave in terms of coins that are invalid (and + * thus available for refresh)? Should be significantly larger + * than #REFRESH_SLOTS_NEEDED, and must be below #pool_size. */ -static unsigned int pool_size = 100; +#define INVALID_COIN_SLACK 20 /** - * Configuration file path + * How much slack must we have to do a refresh? Should be the + * maximum number of coins a refresh can generate, and thus + * larger than log(base 2) of #COIN_VALUE. Must also be + * smaller than #INVALID_COIN_SLACK and smaller than 64. */ -static char *config_file; +#define REFRESH_SLOTS_NEEDED 5 /** - * Configuation object (used to get BANK_URI) + * The benchmark withdraws always the same denomination, since the + * calculation for refreshing is statically done (at least in this + * first version). In the future, this will be the largest value + * we ever withdraw. */ -struct GNUNET_CONFIGURATION_Handle *cfg; +#define COIN_VALUE 8 /** - * How many reserves ought to be created given the pool size + * Probability a coin can be refreshed. + * This probability multiplied by the number of coins + * generated during the average refresh must be smaller + * than one. The variance must be covered by the + * #INVALID_COIN_SLACK. */ -static unsigned int nreserves; +#define REFRESH_PROBABILITY 0.1 /** - * How many coins are in `coins` array. This is needed - * as the number of coins is not always nreserves * COINS_PER_RESERVE - * due to refresh operations + * What is the amount we deposit into a reserve each time. + * We keep it simple and always deposit the same amount for now. */ -unsigned int ncoins; - +#define RESERVE_VALUE 1000 /** - * Bank details of who creates reserves + * What should be the ratio of coins withdrawn per reserve? + * We roughly match #RESERVE_VALUE / #COIN_VALUE, as that + * matches draining the reserve. */ -json_t *sender_details; +#define COINS_PER_RESERVE 12 /** - * Bank details of who deposits coins + * How many times must #benchmark_run() execute before we + * consider ourselves warm? */ -json_t *merchant_details; +#define WARM_THRESHOLD 1000LL /** - * Information needed by the /refresh/melt's callback + * List of coins to get in return to a melt operation, in order + * of preference. The values from this structure are converted + * to the #refresh_pk array. Must be NULL-terminated. The + * currency is omitted as we get that from /keys. */ -struct RefreshRevealCls { +static const char *refresh_denoms[] = { + "4.00", + "2.00", + "1.00", + NULL +}; - /** - * The result of a `TALER_EXCHANGE_refresh_prepare()` call - */ - const char *blob; +/** + * Needed information for a reserve. Other values are the same for all reserves, therefore defined in global variables + */ +struct Reserve +{ /** - * Size of `blob` + * DLL of reserves to fill. */ - size_t blob_size; + struct Reserve *next; /** - * Which coin in the list are we melting + * DLL of reserves to fill. */ - unsigned int coin_index; -}; + struct Reserve *prev; -/** - * Needed information for a reserve. Other values are the same for all reserves, therefore defined in global variables - */ -struct Reserve { - /** + /** * Set (by the interpreter) to the reserve's private key * we used to fill the reserve. */ @@ -103,44 +119,36 @@ struct Reserve { * Set to the API's handle during the operation. */ struct TALER_EXCHANGE_AdminAddIncomingHandle *aih; + + /** + * How much is left in this reserve. + */ + struct TALER_Amount left; + /** + * Index of this reserve in the #reserves array. + */ + unsigned int reserve_index; + }; -/** - * Array of denomination keys needed to perform the 4 KUDOS - * refresh operation - */ -struct TALER_EXCHANGE_DenomPublicKey *refresh_pk; - -/** - * Size of `refresh_pk` - */ -unsigned int refresh_pk_len; - -/** - * Same blinding key for all coins - */ -struct TALER_DenominationBlindingKeyP blinding_key; /** * Information regarding a coin */ -struct Coin { +struct Coin +{ + /** - * Index in the reserve's global array indicating which - * reserve this coin is to be retrieved. If the coin comes - * from a refresh, then this value is set to the melted coin's - * reserve index + * DLL of coins to withdraw. */ - unsigned int reserve_index; + struct Coin *next; /** - * If @e amount is NULL, this specifies the denomination key to - * use. Otherwise, this will be set (by the interpreter) to the - * denomination PK matching @e amount. + * DLL of coins to withdraw. */ - const struct TALER_EXCHANGE_DenomPublicKey *pk; - + struct Coin *prev; + /** * Set (by the interpreter) to the exchange's signature over the * coin's public key. @@ -148,39 +156,173 @@ struct Coin { struct TALER_DenominationSignature sig; /** - * Set (by the interpreter) to the coin's private key. + * Set to the coin's private key. */ struct TALER_CoinSpendPrivateKeyP coin_priv; /** + * This specifies the denomination key to use. + */ + const struct TALER_EXCHANGE_DenomPublicKey *pk; + + /** * Withdraw handle (while operation is running). */ struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh; /** + * Refresh melt handle + */ + struct TALER_EXCHANGE_RefreshMeltHandle *rmh; + + /** + * Refresh reveal handle + */ + struct TALER_EXCHANGE_RefreshRevealHandle *rrh; + + /** * Deposit handle (while operation is running). */ struct TALER_EXCHANGE_DepositHandle *dh; /** + * Array of denominations we expect to get from melt. + */ + struct TALER_Amount *denoms; + + /** + * The result of a #TALER_EXCHANGE_refresh_prepare() call + */ + char *blob; + + /** + * Size of @e blob + */ + size_t blob_size; + + /** * Flag indicating if the coin is going to be refreshed */ unsigned int refresh; /** - * Refresh melt handle + * #GNUNET_YES if this coin is in the #invalid_coins_head DLL. */ - struct TALER_EXCHANGE_RefreshMeltHandle *rmh; + int invalid; + + /** + * Index in the reserve's global array indicating which + * reserve this coin is to be retrieved. If the coin comes + * from a refresh, then this value is set to the melted coin's + * reserve index + */ + unsigned int reserve_index; /** - * Refresh reveal handle + * Index of this coin in the #coins array. + */ + unsigned int coin_index; + + /** + * If the coin has to be refreshed, this value indicates + * how much is left on this coin */ - struct TALER_EXCHANGE_RefreshRevealHandle *rrh; + struct TALER_Amount left; }; /** + * DLL of reserves to fill. + */ +static struct Reserve *empty_reserve_head; + +/** + * DLL of reserves to fill. + */ +static struct Reserve *empty_reserve_tail; + +/** + * DLL of coins to withdraw. + */ +static struct Coin *invalid_coins_head; + +/** + * DLL of coins to withdraw. + */ +static struct Coin *invalid_coins_tail; + +/** + * How many coins are in the #invalid_coins_head DLL? + */ +static unsigned int num_invalid_coins; + +/** + * Should we initialize and start the exchange, if #GNUNET_NO, + * we expect one to be already up and running. + */ +static int run_exchange; + +/** + * Enables printing of "C" and "W" to indicate progress (warm/cold) + * every 50 iterations. Also includes how long the iteration took, + * so we can see if it is stable. + */ +static int be_verbose; + +/** + * How many coins the benchmark should operate on + */ +static unsigned int pool_size = 100; + +/** + * Configuration file path + */ +static char *config_file; + +/** + * Configuation object (used to get BANK_URI) + */ +static struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * How many reserves ought to be created given the pool size + */ +static unsigned int nreserves; + +/** + * How many coins are in the #coins array. This is needed + * as the number of coins is not always #nreserves * #COINS_PER_RESERVE + * due to refresh operations + */ +static unsigned int ncoins; + +/** + * Bank details of who creates reserves + */ +static json_t *bank_details; + +/** + * Bank details of who deposits coins + */ +static json_t *merchant_details; + +/** + * Array of denomination keys needed to perform the refresh operation + */ +static struct TALER_EXCHANGE_DenomPublicKey *refresh_pk; + +/** + * Size of #refresh_pk + */ +static unsigned int refresh_pk_len; + +/** + * Same blinding key for all coins + */ +static struct TALER_DenominationBlindingKeyP blinding_key; + +/** * Handle to the exchange's process */ static struct GNUNET_OS_Process *exchanged; @@ -193,7 +335,7 @@ static struct GNUNET_CURL_RescheduleContext *rc; /** * Benchmark's task */ -struct GNUNET_SCHEDULER_Task *benchmark_task; +static struct GNUNET_SCHEDULER_Task *benchmark_task; /** * Main execution context for the main loop of the exchange. @@ -206,29 +348,24 @@ static struct GNUNET_CURL_Context *ctx; static struct TALER_EXCHANGE_Handle *exchange; /** - * The array of all reserves + * The array of all reserves, of length #nreserves. */ static struct Reserve *reserves; /** - * The array of all coins + * The array of all coins, of length #ncoins. */ static struct Coin *coins; /** - * Indices of spent coins - */ -static unsigned int *spent_coins; - -/** - * Current number of spent coins + * Transaction id counter, used in /deposit's */ -static unsigned int spent_coins_size = 0; +static unsigned int transaction_id; /** - * Transaction id counter, used in /deposit's + * Transfer UUID counter, used in /admin/add/incoming */ -static unsigned int transaction_id = 0; +static unsigned int transfer_uuid; /** * This key (usually provided by merchants) is needed when depositing coins, @@ -239,12 +376,13 @@ static struct TALER_MerchantPrivateKeyP merchant_priv; /** * URI under which the exchange is reachable during the benchmark. */ -#define EXCHANGE_URI "http://localhost:8081/" +static char *exchange_uri; /** - * How many coins (AKA withdraw operations) per reserve should be withdrawn + * URI under which the administrative exchange is reachable during the + * benchmark. */ -#define COINS_PER_RESERVE 12 +static char *exchange_admin_uri; /** * Used currency (read from /keys' output) @@ -252,64 +390,61 @@ static struct TALER_MerchantPrivateKeyP merchant_priv; static char *currency; /** - * Large enough value to allow having 12 coins per reserve without parsing - * /keys in the first place + * What time did we start to really measure performance? */ -#define RESERVE_VALUE 1000 +static struct GNUNET_TIME_Absolute start_time; /** - * The benchmark withdraws always the same denomination, since the calculation - * for refreshing is statically done (at least in its very first version). - */ -#define COIN_VALUE 8 + * Number of times #bennchmark_run has executed. Used + * to indicate when we consider us warm. + */ +static unsigned long long warm; + +/** + * Number of times #bennchmark_run should execute + * before we shut down. + */ +static unsigned int num_iterations; /** - * Probability a coin can be spent + * Number of /deposit operations we have executed since #start_time. */ -#define SPEND_PROBABILITY 0.1 +static unsigned long long num_deposit; /** - * Probability a coin can be refreshed + * Number of /withdraw operations we have executed since #start_time. */ -#define REFRESH_PROBABILITY 0.4 +static unsigned long long num_withdraw; /** - * Refreshed once. For each batch of deposits, only one - * coin will be refreshed, according to #REFRESH_PROBABILITY + * Number of /refresh operations we have executed since #start_time. */ -static unsigned int refreshed_once = GNUNET_NO; +static unsigned long long num_refresh; /** - * List of coins to get in return to a melt operation. Just a - * static list for now as every melt operation is carried out - * on a 8 KUDOS coin whose only 1 KUDOS has been spent, thus - * 7 KUDOS melted. This structure must be changed with one holding - * TALER_Amount structs, as every time it's needed it requires - * too many operations before getting the desired TALER_Amount. + * Number of /admin operations we have executed since #start_time. */ -static char *refresh_denoms[] = { - "4", - "2", - "1", - NULL -}; +static unsigned long long num_admin; + +/** + * Throw a weighted coin with @a probability. + * + * @reurn #GNUNET_OK with @a probability, #GNUNET_NO with 1 - @a probability + */ static unsigned int eval_probability (float probability) { - unsigned int random; + uint64_t random; float random_01; - random = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX); - random_01 = (float) random / UINT32_MAX; - return random_01 <= probability ? GNUNET_OK : GNUNET_NO; + random = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT64_MAX); + random_01 = (double) random / UINT64_MAX; + return (random_01 <= probability) ? GNUNET_OK : GNUNET_NO; } -static void -do_shutdown (void *cls); - - /** * Shutdown benchmark in case of errors * @@ -323,7 +458,26 @@ fail (const char *msg) "%s\n", msg); GNUNET_SCHEDULER_shutdown (); - return; +} + + +/** + * Main task for the benchmark. + * + * @param cls NULL + */ +static void +benchmark_run (void *cls); + + +/** + * Run the main task for the benchmark. + */ +static void +continue_master_task () +{ + benchmark_task = GNUNET_SCHEDULER_add_now (&benchmark_run, + NULL); } @@ -380,10 +534,11 @@ find_pk (const struct TALER_EXCHANGE_Keys *keys, return NULL; } + /** * Function called with the result of the /refresh/reveal operation. * - * @param cls closure with the interpreter state + * @param cls closure with the `struct Coin *` * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request * 0 if the exchange's reply is bogus (fails to follow the protocol) * @param num_coins number of fresh coins created, length of the @a sigs and @a coin_privs arrays, 0 if the operation failed @@ -399,50 +554,67 @@ reveal_cb (void *cls, const struct TALER_DenominationSignature *sigs, const json_t *full_response) { - struct RefreshRevealCls *rrcls = cls; + struct Coin *coin = cls; unsigned int i; const struct TALER_EXCHANGE_Keys *keys; - coins[rrcls->coin_index].rrh = NULL; + coin->rrh = NULL; if (MHD_HTTP_OK != http_status) { - GNUNET_free (rrcls); json_dumpf (full_response, stderr, 0); - fail ("Not all coins correctly revealed\n"); + fail ("Not all coins correctly revealed"); return; } else + { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Coin #%d revealed!\n", - rrcls->coin_index); + coin->coin_index); + coin->left.value = 0; + } + keys = TALER_EXCHANGE_get_keys (exchange); for (i=0; i<num_coins; i++) { - struct Coin fresh_coin; - struct TALER_Amount amount; - char *refresh_denom; - - GNUNET_asprintf (&refresh_denom, - "%s:%s", - currency, - refresh_denoms[i]); - fresh_coin.reserve_index = coins[rrcls->coin_index].reserve_index; - TALER_string_to_amount (refresh_denom, &amount); - GNUNET_free (refresh_denom); - fresh_coin.pk = find_pk (keys, &amount); - fresh_coin.sig = sigs[i]; - GNUNET_array_append (coins, ncoins, fresh_coin); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "# of coins after refresh: %d\n", + struct Coin *fresh_coin; + char *revealed_str; + + revealed_str = TALER_amount_to_string (&coin->denoms[i]); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "revealing %s # of coins after refresh: %d\n", + revealed_str, ncoins); + GNUNET_free (revealed_str); + + fresh_coin = invalid_coins_head; + if (NULL == fresh_coin) + { + /* #REFRESH_SLOTS_NEEDED too low? */ + GNUNET_break (0); + continue; + } + GNUNET_CONTAINER_DLL_remove (invalid_coins_head, + invalid_coins_tail, + fresh_coin); + num_invalid_coins--; + fresh_coin->invalid = GNUNET_NO; + fresh_coin->pk = find_pk (keys, &coin->denoms[i]); + GNUNET_assert (NULL == fresh_coin->sig.rsa_signature); + fresh_coin->sig.rsa_signature = + GNUNET_CRYPTO_rsa_signature_dup (sigs[i].rsa_signature); + fresh_coin->coin_priv = coin_privs[i]; + fresh_coin->left = coin->denoms[i]; } - GNUNET_free (rrcls); + GNUNET_free (coin->denoms); + coin->denoms = NULL; + continue_master_task (); } + /** * Function called with the result of the /refresh/melt operation. * - * @param cls closure with the interpreter state + * @param cls closure with the `struct Coin *` * @param http_status HTTP response code, never #MHD_HTTP_OK (200) as for successful intermediate response this callback is skipped. * 0 if the exchange's reply is bogus (fails to follow the protocol) * @param noreveal_index choice by the exchange in the cut-and-choose protocol, @@ -457,47 +629,150 @@ melt_cb (void *cls, const struct TALER_ExchangePublicKeyP *exchange_pub, const json_t *full_response) { - struct RefreshRevealCls *rrcls = cls; - /* FIXME to be freed */ + struct Coin *coin = cls; - coins[rrcls->coin_index].rmh = NULL; + coin->rmh = NULL; if (MHD_HTTP_OK != http_status) { json_dumpf (full_response, stderr, 0); - fail ("Coin not correctly melted!\n"); + fail ("Coin not correctly melted!"); return; } - coins[rrcls->coin_index].rrh + + coin->rrh = TALER_EXCHANGE_refresh_reveal (exchange, - rrcls->blob_size, - rrcls->blob, + coin->blob_size, + coin->blob, noreveal_index, - reveal_cb, - rrcls); + &reveal_cb, + coin); + GNUNET_free (coin->blob); + coin->blob = NULL; + if (NULL == coin->rrh) + { + fail ("Failed on reveal during refresh!"); + return; + } } /** - * Function called upon completion of our /reserve/withdraw request. - * This is merely the function which spends withdrawn coins + * Mark coin as invalid. * - * @param cls closure with the interpreter state - * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request - * 0 if the exchange's reply is bogus (fails to follow the protocol) - * @param sig signature over the coin, NULL on error - * @param full_response full response from the exchange (for logging, in case of errors) + * @param coin coin to mark invalid */ static void -reserve_withdraw_cb (void *cls, - unsigned int http_status, - const struct TALER_DenominationSignature *sig, - const json_t *full_response); +invalidate_coin (struct Coin *coin) +{ + GNUNET_CONTAINER_DLL_insert (invalid_coins_head, + invalid_coins_tail, + coin); + num_invalid_coins++; + coin->invalid = GNUNET_YES; + if (NULL != coin->sig.rsa_signature) + { + GNUNET_CRYPTO_rsa_signature_free (coin->sig.rsa_signature); + coin->sig.rsa_signature = NULL; + } +} + + +/** + * Refresh the given @a coin + * + * @param coin coin to refresh + */ +static void +refresh_coin (struct Coin *coin) +{ + char *blob; + size_t blob_size; + struct TALER_Amount *denoms = NULL; + struct TALER_EXCHANGE_DenomPublicKey *dpks = NULL; + const struct TALER_EXCHANGE_DenomPublicKey *curr_dpk; + struct TALER_Amount curr; + struct TALER_Amount left; + unsigned int ndenoms = 0; + unsigned int ndenoms2 = 0; + unsigned int off; + + GNUNET_break (NULL == coin->denoms); + TALER_amount_get_zero (currency, &curr); + left = coin->left; + off = 0; + while (0 != TALER_amount_cmp (&curr, + &left)) + { + if (off >= refresh_pk_len) + { + /* refresh currency choices do not add up! */ + GNUNET_break (0); + break; + } + curr_dpk = &refresh_pk[off]; + while (-1 != TALER_amount_cmp (&left, + &curr_dpk->value)) + { + GNUNET_array_append (denoms, + ndenoms, + curr_dpk->value); + GNUNET_array_append (dpks, + ndenoms2, + *curr_dpk); + GNUNET_assert (GNUNET_SYSERR != + TALER_amount_subtract (&left, + &left, + &curr_dpk->value)); + } + off++; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "# of coins to get in melt: %d\n", + ndenoms2); + GNUNET_break (ndenoms2 <= REFRESH_SLOTS_NEEDED); + blob = TALER_EXCHANGE_refresh_prepare (&coin->coin_priv, + &coin->left, + &coin->sig, + coin->pk, + GNUNET_YES, + ndenoms2, + dpks, + &blob_size); + invalidate_coin (coin); + GNUNET_array_grow (dpks, + ndenoms2, + 0); + if (NULL == blob) + { + fail ("Failed to prepare refresh"); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Prepared blob of size %d for refresh\n", + (unsigned int) blob_size); + + coin->blob = blob; + coin->blob_size = blob_size; + coin->denoms = denoms; + if (warm >= WARM_THRESHOLD) + num_refresh++; + coin->rmh = TALER_EXCHANGE_refresh_melt (exchange, + blob_size, + blob, + &melt_cb, + coin); + if (NULL == coin->rmh) + { + fail ("Impossible to issue a melt request to the exchange"); + return; + } +} /** * Function called with the result of a /deposit operation. * - * @param cls closure with the interpreter state + * @param cls closure with the `struct Coin` that we are processing * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful deposit; * 0 if the exchange's reply is bogus (fails to follow the protocol) * @param exchange_pub public key used by the exchange for signing @@ -510,88 +785,149 @@ deposit_cb (void *cls, const struct TALER_ExchangePublicKeyP *exchange_pub, const json_t *obj) { - unsigned int coin_index = (unsigned int) (long) cls; + struct Coin *coin = cls; - coins[coin_index].dh = NULL; + coin->dh = NULL; if (MHD_HTTP_OK != http_status) { json_dumpf (obj, stderr, 0); - fail ("At least one coin has not been deposited, status: %d\n"); + fail ("At least one coin has not been deposited, status: %d"); return; } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Coin #%d correctly spent!\n", coin_index); - GNUNET_array_append (spent_coins, spent_coins_size, coin_index); - spent_coins_size++; - if (GNUNET_YES == coins[coin_index].refresh) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Coin #%d correctly spent!\n", + coin->coin_index); + if (GNUNET_YES == coin->refresh) { - struct TALER_Amount melt_amount; - struct RefreshRevealCls *rrcls; - - TALER_amount_get_zero (currency, &melt_amount); - melt_amount.value = 7; - char *blob; - size_t blob_size; - - blob = TALER_EXCHANGE_refresh_prepare (&coins[coin_index].coin_priv, - &melt_amount, - &coins[coin_index].sig, - coins[coin_index].pk, - GNUNET_YES, - refresh_pk_len, - refresh_pk, - &blob_size); - if (NULL == blob) - { - fail ("Failed to prepare refresh"); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "prepared blob %d\n", - (unsigned int) blob_size); - refreshed_once = GNUNET_YES; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "# of coins to get in melt: %d\n", - refresh_pk_len); - rrcls = GNUNET_new (struct RefreshRevealCls); - rrcls->blob = blob; - rrcls->blob_size = blob_size; - rrcls->coin_index = coin_index; - - coins[coin_index].rmh = TALER_EXCHANGE_refresh_melt (exchange, - blob_size, - blob, - &melt_cb, - rrcls); - if (NULL == coins[coin_index].rmh) - { - fail ("Impossible to issue a melt request to the exchange\n"); - return; - } + refresh_coin (coin); + } + else + { + invalidate_coin (coin); + continue_master_task (); + } +} + + +/** + * Spend the given coin. Also triggers refresh + * with a certain probability. + * + * @param coin coin to spend + * @param do_refresh should we also do the refresh? + */ +static void +spend_coin (struct Coin *coin, + int do_refresh) +{ + struct TALER_Amount amount; + struct GNUNET_TIME_Absolute wire_deadline; + struct GNUNET_TIME_Absolute timestamp; + struct GNUNET_TIME_Absolute refund_deadline; + struct GNUNET_HashCode h_contract; + struct TALER_CoinSpendPublicKeyP coin_pub; + struct TALER_DepositRequestPS dr; + struct TALER_MerchantPublicKeyP merchant_pub; + struct TALER_CoinSpendSignatureP coin_sig; + + GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv, + &coin_pub.eddsa_pub); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &h_contract, + sizeof (h_contract)); + timestamp = GNUNET_TIME_absolute_get (); + wire_deadline = GNUNET_TIME_absolute_add (timestamp, + GNUNET_TIME_UNIT_WEEKS); + refund_deadline = GNUNET_TIME_absolute_add (timestamp, + GNUNET_TIME_UNIT_DAYS); + GNUNET_TIME_round_abs (×tamp); + GNUNET_TIME_round_abs (&wire_deadline); + GNUNET_TIME_round_abs (&refund_deadline); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Spending %d-th coin\n", + coin->coin_index); + + if (do_refresh) + { + /** + * Always spending 1 out of 8 KUDOS. To be improved by randomly + * picking the spent amount + */ + struct TALER_Amount one; + + TALER_amount_get_zero (currency, &one); + one.value = 1; + + TALER_amount_subtract (&amount, + &one, + &coin->pk->fee_deposit); + TALER_amount_subtract (&coin->left, + &coin->pk->value, + &one); + coin->refresh = GNUNET_YES; } else - { /* re-withdraw */ - struct GNUNET_CRYPTO_EddsaPrivateKey *coin_priv; - coin_priv = GNUNET_CRYPTO_eddsa_key_create (); - coins[coin_index].coin_priv.eddsa_priv = *coin_priv; - GNUNET_free (coin_priv); - coins[coin_index].wsh = - TALER_EXCHANGE_reserve_withdraw (exchange, - coins[coin_index].pk, - &reserves[coins[coin_index].reserve_index].reserve_priv, - &coins[coin_index].coin_priv, - &blinding_key, - reserve_withdraw_cb, - (void *) (long) coin_index); + { + TALER_amount_subtract (&amount, + &coin->pk->value, + &coin->pk->fee_deposit); + coin->refresh = GNUNET_NO; + } + memset (&dr, 0, sizeof (dr)); + dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS)); + dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); + dr.h_contract = h_contract; + TALER_JSON_hash (merchant_details, + &dr.h_wire); + + dr.timestamp = GNUNET_TIME_absolute_hton (timestamp); + dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline); + dr.transaction_id = GNUNET_htonll (transaction_id); + + TALER_amount_hton (&dr.amount_with_fee, + &amount); + TALER_amount_hton (&dr.deposit_fee, + &coin->pk->fee_deposit); + + GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv.eddsa_priv, + &merchant_pub.eddsa_pub); + dr.merchant = merchant_pub; + dr.coin_pub = coin_pub; + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_eddsa_sign (&coin->coin_priv.eddsa_priv, + &dr.purpose, + &coin_sig.eddsa_signature)); + if (warm >= WARM_THRESHOLD) + num_deposit++; + coin->dh = TALER_EXCHANGE_deposit (exchange, + &amount, + wire_deadline, + merchant_details, + &h_contract, + &coin_pub, + &coin->sig, + &coin->pk->key, + timestamp, + transaction_id++, + &merchant_pub, + refund_deadline, + &coin_sig, + &deposit_cb, + coin); + if (NULL == coin->dh) + { + fail ("An error occurred while calling deposit API"); + return; } } /** * Function called upon completion of our /reserve/withdraw request. - * This is merely the function which spends withdrawn coins + * This is merely the function which spends withdrawn coins. For each + * spent coin, it either refresh it or re-withdraw it. * - * @param cls closure with the interpreter state + * @param cls closure with our `struct Coin` * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request * 0 if the exchange's reply is bogus (fails to follow the protocol) * @param sig signature over the coin, NULL on error @@ -603,127 +939,86 @@ reserve_withdraw_cb (void *cls, const struct TALER_DenominationSignature *sig, const json_t *full_response) { + struct Coin *coin = cls; - unsigned int coin_index = (unsigned int) (long) cls; - - coins[coin_index].wsh = NULL; + coin->wsh = NULL; if (MHD_HTTP_OK != http_status) { json_dumpf (full_response, stderr, 0); - fail ("At least one coin has not correctly been withdrawn\n"); + fail ("At least one coin has not correctly been withdrawn"); return; } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%d-th coin withdrawn\n", - coin_index); - coins[coin_index].sig.rsa_signature = + coin->coin_index); + coin->sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature); - if (GNUNET_OK == eval_probability (SPEND_PROBABILITY)) + GNUNET_CONTAINER_DLL_remove (invalid_coins_head, + invalid_coins_tail, + coin); + num_invalid_coins--; + coin->invalid = GNUNET_NO; + continue_master_task (); +} + + +/** + * Withdraw the given coin from the respective reserve. + * + * @param coin coin to withdraw + */ +static void +withdraw_coin (struct Coin *coin) +{ + struct GNUNET_CRYPTO_EddsaPrivateKey *coin_priv; + struct TALER_Amount amount; + struct TALER_Amount left; + const struct TALER_EXCHANGE_Keys *keys; + struct Reserve *r; + + keys = TALER_EXCHANGE_get_keys (exchange); + r = &reserves[coin->reserve_index]; + coin_priv = GNUNET_CRYPTO_eddsa_key_create (); + coin->coin_priv.eddsa_priv = *coin_priv; + GNUNET_free (coin_priv); + TALER_amount_get_zero (currency, + &amount); + amount.value = COIN_VALUE; + GNUNET_assert (-1 != TALER_amount_cmp (&r->left, + &amount)); + GNUNET_assert (NULL != (coin->pk = find_pk (keys, &amount))); + if (warm >= WARM_THRESHOLD) + num_withdraw++; + coin->wsh = + TALER_EXCHANGE_reserve_withdraw (exchange, + coin->pk, + &r->reserve_priv, + &coin->coin_priv, + &blinding_key, + &reserve_withdraw_cb, + coin); + GNUNET_assert (GNUNET_SYSERR != + TALER_amount_subtract (&left, + &r->left, + &amount)); + r->left = left; + if (-1 == TALER_amount_cmp (&left, + &amount)) { - struct TALER_Amount amount; - struct GNUNET_TIME_Absolute wire_deadline; - struct GNUNET_TIME_Absolute timestamp; - struct GNUNET_TIME_Absolute refund_deadline; - struct GNUNET_HashCode h_contract; - struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_DepositRequestPS dr; - struct TALER_MerchantPublicKeyP merchant_pub; - struct TALER_CoinSpendSignatureP coin_sig; - - GNUNET_CRYPTO_eddsa_key_get_public (&coins[coin_index].coin_priv.eddsa_priv, - &coin_pub.eddsa_pub); - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - &h_contract, - sizeof (h_contract)); - timestamp = GNUNET_TIME_absolute_get (); - wire_deadline = GNUNET_TIME_absolute_add (timestamp, GNUNET_TIME_UNIT_WEEKS); - refund_deadline = GNUNET_TIME_absolute_add (timestamp, GNUNET_TIME_UNIT_DAYS); - GNUNET_TIME_round_abs (×tamp); - GNUNET_TIME_round_abs (&wire_deadline); - GNUNET_TIME_round_abs (&refund_deadline); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Spending %d-th coin\n", coin_index); - - if (GNUNET_YES == eval_probability (REFRESH_PROBABILITY) - && GNUNET_NO == refreshed_once) - { - struct TALER_Amount one; - TALER_amount_get_zero (currency, &one); - one.value = 1; - - /** - * If the coin is going to be refreshed, only 1 unit - * of currency will be spent, since 4 units are going - * to be refreshed - */ - TALER_amount_subtract (&amount, - &one, - &coins[coin_index].pk->fee_deposit); - coins[coin_index].refresh = GNUNET_YES; - refreshed_once = GNUNET_YES; - } - else - { - TALER_amount_subtract (&amount, - &coins[coin_index].pk->value, - &coins[coin_index].pk->fee_deposit); - } - memset (&dr, 0, sizeof (dr)); - dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS)); - dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); - dr.h_contract = h_contract; - TALER_JSON_hash (merchant_details, - &dr.h_wire); - - dr.timestamp = GNUNET_TIME_absolute_hton (timestamp); - dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline); - dr.transaction_id = GNUNET_htonll (transaction_id); - - TALER_amount_hton (&dr.amount_with_fee, - &amount); - TALER_amount_hton (&dr.deposit_fee, - &coins[coin_index].pk->fee_deposit); - - GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv.eddsa_priv, - &merchant_pub.eddsa_pub); - dr.merchant = merchant_pub; - dr.coin_pub = coin_pub; - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_sign (&coins[coin_index].coin_priv.eddsa_priv, - &dr.purpose, - &coin_sig.eddsa_signature)); - - coins[coin_index].dh = TALER_EXCHANGE_deposit (exchange, - &amount, - wire_deadline, - merchant_details, - &h_contract, - &coin_pub, - &coins[coin_index].sig, - &coins[coin_index].pk->key, - timestamp, - transaction_id, - &merchant_pub, - refund_deadline, - &coin_sig, - &deposit_cb, - (void *) (long) coin_index); - if (NULL == coins[coin_index].dh) - { - json_decref (merchant_details); - fail ("An error occurred while calling deposit API\n"); - return; - } - transaction_id++; + /* not enough left in the reserve for future withdrawals, + create a new reserve! */ + GNUNET_CONTAINER_DLL_insert (empty_reserve_head, + empty_reserve_tail, + r); } } - /** * Function called upon completion of our /admin/add/incoming request. * Its duty is withdrawing coins on the freshly created reserve. * - * @param cls closure with the interpreter state + * @param cls closure with the `struct Reserve *` * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request * 0 if the exchange's reply is bogus (fails to follow the protocol) * @param full_response full response from the exchange (for logging, in case of errors) @@ -733,126 +1028,158 @@ add_incoming_cb (void *cls, unsigned int http_status, const json_t *full_response) { - unsigned int reserve_index = (unsigned int) (long) cls; - struct GNUNET_CRYPTO_EddsaPrivateKey *coin_priv; - unsigned int i; - unsigned int coin_index; - struct TALER_Amount amount; - const struct TALER_EXCHANGE_Keys *keys; + struct Reserve *r = cls; - keys = TALER_EXCHANGE_get_keys (exchange); + r->aih = NULL; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "/admin/add/incoming callback called on %d-th reserve\n", - reserve_index); - reserves[reserve_index].aih = NULL; + r->reserve_index); if (MHD_HTTP_OK != http_status) { json_dumpf (full_response, stderr, 0); - fail ("At least one reserve failed in being created\n"); - } - - for (i=0; i < COINS_PER_RESERVE; i++) - { - coin_priv = GNUNET_CRYPTO_eddsa_key_create (); - coin_index = reserve_index * COINS_PER_RESERVE + i; - coins[coin_index].coin_priv.eddsa_priv = *coin_priv; - coins[coin_index].reserve_index = reserve_index; - TALER_amount_get_zero (currency, &amount); - amount.value = COIN_VALUE; - GNUNET_assert (NULL != (coins[coin_index].pk = find_pk (keys, &amount))); - GNUNET_free (coin_priv); - coins[coin_index].wsh = - TALER_EXCHANGE_reserve_withdraw (exchange, - coins[coin_index].pk, - &reserves[reserve_index].reserve_priv, - &coins[coin_index].coin_priv, - &blinding_key, - reserve_withdraw_cb, - (void *) (long) coin_index); + fail ("At least one reserve failed in being created"); + return; } + GNUNET_CONTAINER_DLL_remove (empty_reserve_head, + empty_reserve_tail, + r); + continue_master_task (); } /** - * Benchmark runner. + * Fill a reserve using /admin/add/incoming * - * @param cls closure for benchmark_run() + * @param r reserve to fill */ static void -benchmark_run (void *cls) +fill_reserve (struct Reserve *r) { - unsigned int i; struct GNUNET_CRYPTO_EddsaPrivateKey *priv; - json_t *transfer_details; - char *uuid; struct TALER_ReservePublicKeyP reserve_pub; struct GNUNET_TIME_Absolute execution_date; struct TALER_Amount reserve_amount; + json_t *transfer_details; - priv = GNUNET_CRYPTO_eddsa_key_create (); - merchant_priv.eddsa_priv = *priv; - GNUNET_free (priv); - - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - &blinding_key, - sizeof (blinding_key)); - TALER_amount_get_zero (currency, &reserve_amount); + TALER_amount_get_zero (currency, + &reserve_amount); reserve_amount.value = RESERVE_VALUE; execution_date = GNUNET_TIME_absolute_get (); GNUNET_TIME_round_abs (&execution_date); + + priv = GNUNET_CRYPTO_eddsa_key_create (); + r->reserve_priv.eddsa_priv = *priv; + GNUNET_free (priv); + transfer_details = json_pack ("{s:I}", + "uuid", (json_int_t) transfer_uuid++); + GNUNET_assert (NULL != transfer_details); + GNUNET_CRYPTO_eddsa_key_get_public (&r->reserve_priv.eddsa_priv, + &reserve_pub.eddsa_pub); + r->left = reserve_amount; + if (warm >= WARM_THRESHOLD) + num_admin++; + r->aih = TALER_EXCHANGE_admin_add_incoming (exchange, + exchange_admin_uri, + &reserve_pub, + &reserve_amount, + execution_date, + bank_details, + transfer_details, + &add_incoming_cb, + r); + GNUNET_assert (NULL != r->aih); + json_decref (transfer_details); +} - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "benchmark_run() invoked\n"); - nreserves = pool_size / COINS_PER_RESERVE; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "creating %d reserves\n", - nreserves); - reserves = GNUNET_new_array (nreserves, - struct Reserve); - ncoins = COINS_PER_RESERVE * nreserves; - coins = GNUNET_new_array (ncoins, - struct Coin); +/** + * Main task for the benchmark. + * + * @param cls NULL + */ +static void +benchmark_run (void *cls) +{ + unsigned int i; + int refresh; + struct Coin *coin; + + benchmark_task = NULL; + /* First, always make sure all reserves are full */ + if (NULL != empty_reserve_head) + { + fill_reserve (empty_reserve_head); + return; + } + /* Second, withdraw until #num_invalid_coins is less than + #INVALID_COIN_SLACK */ + if (num_invalid_coins > INVALID_COIN_SLACK) + { + withdraw_coin (invalid_coins_head); + return; + } + warm++; + if ( be_verbose && + (0 == (warm % 50)) ) + { + static struct GNUNET_TIME_Absolute last; + struct GNUNET_TIME_Relative duration; - for (i=0;i < nreserves && 0 < nreserves;i++) + if (0 != last.abs_value_us) + duration = GNUNET_TIME_absolute_get_duration (last); + else + duration = GNUNET_TIME_UNIT_FOREVER_REL; + last = GNUNET_TIME_absolute_get (); + fprintf (stderr, + "%s - %s\n", + WARM_THRESHOLD < warm ? "WARM" : "COLD", + GNUNET_STRINGS_relative_time_to_string (duration, + GNUNET_YES)); + } + if (WARM_THRESHOLD == warm) { - priv = GNUNET_CRYPTO_eddsa_key_create (); - reserves[i].reserve_priv.eddsa_priv = *priv; - GNUNET_free (priv); - GNUNET_asprintf (&uuid, "{ \"uuid\":%d}", i); - transfer_details = json_loads (uuid, JSON_REJECT_DUPLICATES, NULL); - GNUNET_free (uuid); - GNUNET_CRYPTO_eddsa_key_get_public (&reserves[i].reserve_priv.eddsa_priv, - &reserve_pub.eddsa_pub); - - reserves[i].aih = TALER_EXCHANGE_admin_add_incoming (exchange, - "http://localhost:18080/", - &reserve_pub, - &reserve_amount, - execution_date, - sender_details, - transfer_details, - &add_incoming_cb, - (void *) (long) i); - GNUNET_assert (NULL != reserves[i].aih); - json_decref (transfer_details); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Benchmark warm.\n"); + start_time = GNUNET_TIME_absolute_get (); } - json_decref (sender_details); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "benchmark_run() returns\n"); + if ( (warm > num_iterations) && + (0 != num_iterations) ) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + + /* By default, pick a random valid coin to spend */ + for (i=0;i<1000;i++) + { + coin = &coins[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + ncoins)]; + if (GNUNET_YES == coin->invalid) + continue; /* unlucky draw, try again */ + if (1 == coin->left.value) + refresh = GNUNET_NO; /* cannot refresh, coin is already at unit */ + else + refresh = eval_probability (REFRESH_PROBABILITY); + if (num_invalid_coins < REFRESH_SLOTS_NEEDED) + refresh = GNUNET_NO; + spend_coin (coin, + refresh); + return; + } + fail ("Too many invalid coins, is your INVALID_COIN_SLACK too high?"); } + /** * Populates the global array of denominations which will - * be withdrawn in a refresh operation. It sums up 4 KUDOS, + * be withdrawn in a refresh operation. It sums up 4 #currency units, * since that is the only amount refreshed so far by the benchmark * - * @param NULL-terminated array of value.fraction pairs * @return #GNUNET_OK if the array is correctly built, #GNUNET_SYSERR * otherwise */ static unsigned int -build_refresh (char **list) +build_refresh () { char *amount_str; struct TALER_Amount amount; @@ -860,22 +1187,32 @@ build_refresh (char **list) const struct TALER_EXCHANGE_DenomPublicKey *picked_denom; const struct TALER_EXCHANGE_Keys *keys; + GNUNET_array_grow (refresh_pk, + refresh_pk_len, + 0); keys = TALER_EXCHANGE_get_keys (exchange); - for (i=0; list[i] != NULL; i++) + for (i=0; NULL != refresh_denoms[i]; i++) { - unsigned int size; - GNUNET_asprintf (&amount_str, "%s:%s", currency, list[i]); - TALER_string_to_amount (amount_str, &amount); - picked_denom = find_pk (keys, &amount); + GNUNET_asprintf (&amount_str, + "%s:%s", + currency, + refresh_denoms[i]); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (amount_str, + &amount)); + picked_denom = find_pk (keys, + &amount); if (NULL == picked_denom) { + GNUNET_break (0); + GNUNET_free (amount_str); return GNUNET_SYSERR; } - size = i; - GNUNET_array_append (refresh_pk, size, *picked_denom); + GNUNET_array_append (refresh_pk, + refresh_pk_len, + *picked_denom); GNUNET_free (amount_str); } - refresh_pk_len = i; return GNUNET_OK; } @@ -893,32 +1230,44 @@ cert_cb (void *cls, const struct TALER_EXCHANGE_Keys *_keys) { /* check that keys is OK */ -#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); GNUNET_SCHEDULER_shutdown(); return; } while (0) - ERR (NULL == _keys); - ERR (0 == _keys->num_sign_keys); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Read %u signing keys\n", - _keys->num_sign_keys); - ERR (0 == _keys->num_denom_keys); + if (NULL == _keys) + { + fail ("Exchange returned no keys!"); + return; + } + if ( (0 == _keys->num_sign_keys) || + (0 == _keys->num_denom_keys) ) + { + GNUNET_break (0); + fail ("Bad /keys response"); + return; + } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Read %u denomination keys\n", + "Read %u signing keys and %u denomination keys\n", + _keys->num_sign_keys, _keys->num_denom_keys); -#undef ERR - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Certificate callback invoked, invoking benchmark_run()\n"); + if (NULL != currency) + { + /* we've been here before, still need to update refresh_denoms */ + if (GNUNET_SYSERR == + build_refresh ()) + { + fail ("Initializing denominations failed"); + return; + } + return; + } currency = GNUNET_strdup (_keys->denom_keys[0].value.currency); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Using currency: %s\n", currency); - - if (GNUNET_SYSERR == build_refresh (refresh_denoms)) + if (GNUNET_SYSERR == + build_refresh ()) { - fail(NULL); + fail ("Initializing denominations failed"); return; } - - benchmark_task = GNUNET_SCHEDULER_add_now (&benchmark_run, - NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Using currency: %s\n", + currency); + continue_master_task (); } @@ -932,15 +1281,18 @@ static void do_shutdown (void *cls) { unsigned int i; + struct GNUNET_TIME_Relative duration; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "shutting down..\n"); - - /** - * WARNING: all the non NULL handles must correspond to non completed - * calls (AKA calls for which the callback function has not been called). - * If not, it segfaults - */ - for (i=0; i<nreserves && 0<nreserves; i++) + if (warm >= WARM_THRESHOLD) + duration = GNUNET_TIME_absolute_get_duration (start_time); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Shutting down...\n"); + if (NULL != benchmark_task) + { + GNUNET_SCHEDULER_cancel (benchmark_task); + benchmark_task = NULL; + } + for (i=0; i<nreserves; i++) { if (NULL != reserves[i].aih) { @@ -951,52 +1303,74 @@ do_shutdown (void *cls) reserves[i].aih = NULL; } } - - for (i=0; i<COINS_PER_RESERVE * nreserves && 0<nreserves; i++) + for (i=0; i<COINS_PER_RESERVE * nreserves; i++) { - if (NULL != coins[i].wsh) + struct Coin *coin = &coins[i]; + + if (NULL != coin->wsh) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cancelling %d-th coin withdraw handle\n", i); - TALER_EXCHANGE_reserve_withdraw_cancel(coins[i].wsh); - coins[i].wsh = NULL; + TALER_EXCHANGE_reserve_withdraw_cancel (coin->wsh); + coin->wsh = NULL; } - if (NULL != coins[i].dh) + if (NULL != coin->dh) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cancelling %d-th coin deposit handle\n", i); - TALER_EXCHANGE_deposit_cancel(coins[i].dh); - coins[i].dh = NULL; + TALER_EXCHANGE_deposit_cancel(coin->dh); + coin->dh = NULL; } - if (NULL != coins[i].rmh) + if (NULL != coin->rmh) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cancelling %d-th coin melt handle\n", i); - TALER_EXCHANGE_refresh_melt_cancel(coins[i].rmh); - coins[i].rmh = NULL; + TALER_EXCHANGE_refresh_melt_cancel (coin->rmh); + coin->rmh = NULL; } - if (NULL != coins[i].rrh) + if (NULL != coin->rrh) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cancelling %d-th coin reveal handle\n", i); - TALER_EXCHANGE_refresh_reveal_cancel(coins[i].rrh); - coins[i].rmh = NULL; + TALER_EXCHANGE_refresh_reveal_cancel (coin->rrh); + coin->rmh = NULL; + } + if (NULL != coin->blob) + { + GNUNET_free (coin->blob); + coin->blob = NULL; + } + if (NULL != coin->sig.rsa_signature) + { + GNUNET_CRYPTO_rsa_signature_free (coin->sig.rsa_signature); + coin->sig.rsa_signature = NULL; + } + if (NULL != coin->denoms) + { + GNUNET_free (coin->denoms); + coin->denoms = NULL; } } - - if (NULL != sender_details) - json_decref (sender_details); + if (NULL != bank_details) + { + json_decref (bank_details); + bank_details = NULL; + } if (NULL != merchant_details) + { json_decref (merchant_details); - + merchant_details = NULL; + } GNUNET_free_non_null (reserves); + reserves = NULL; GNUNET_free_non_null (coins); - GNUNET_free_non_null (spent_coins); + coins = NULL; GNUNET_free_non_null (currency); + currency = NULL; if (NULL != exchange) { @@ -1019,90 +1393,160 @@ do_shutdown (void *cls) GNUNET_CURL_gnunet_rc_destroy (rc); rc = NULL; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "All (?) tasks shut down\n"); - GNUNET_OS_process_kill (exchanged, SIGTERM); + GNUNET_CONFIGURATION_destroy (cfg); + cfg = NULL; + if (warm >= WARM_THRESHOLD) + { + fprintf (stderr, + "Executed A=%llu/W=%llu/D=%llu/R=%llu operations in %s\n", + num_admin, + num_withdraw, + num_deposit, + num_refresh, + GNUNET_STRINGS_relative_time_to_string (duration, + GNUNET_YES)); + } + else + { + fprintf (stdout, + "Sorry, no results, benchmark did not get warm!\n"); + } } /** * Main function that will be run by the scheduler. + * Prepares everything for the benchmark. * * @param cls closure */ static void run (void *cls) { - char *sender_details_filename; + char *bank_details_filename; char *merchant_details_filename; + struct GNUNET_CRYPTO_EddsaPrivateKey *priv; + unsigned int i; + unsigned int j; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "running run()\n"); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "gotten pool_size of %d\n", pool_size); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "config file: %s\n", - config_file); - if (NULL == config_file) { - fail ("-c option is mandatory\n"); + fail ("-c option is mandatory"); return; } - /** - * Read sender_details.json here - */ cfg = GNUNET_CONFIGURATION_create (); - if (GNUNET_SYSERR == GNUNET_CONFIGURATION_parse (cfg, config_file)) + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + NULL); + if (GNUNET_SYSERR == GNUNET_CONFIGURATION_parse (cfg, + config_file)) { - fail ("failed to parse configuration file\n"); + fail ("Failed to parse configuration file"); return; } - if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_filename (cfg, - "benchmark", - "sender_details", - &sender_details_filename)) + if (pool_size < INVALID_COIN_SLACK) { - fail ("failed to get SENDER_DETAILS value\n"); + fail ("Pool size given too small."); + return; + } + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_filename (cfg, + "benchmark", + "bank_details", + &bank_details_filename)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "benchmark", + "bank_details"); + fail ("Failed to get BANK_DETAILS value"); return; } - sender_details = json_load_file (sender_details_filename, - JSON_REJECT_DUPLICATES, - NULL); - - if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_filename (cfg, - "benchmark", - "merchant_details", - &merchant_details_filename)) + bank_details = json_load_file (bank_details_filename, + JSON_REJECT_DUPLICATES, + NULL); + GNUNET_free (bank_details_filename); + if (NULL == bank_details) { - fail ("failed to get MERCHANT_DETAILS value\n"); + fail ("Failed to parse file with BANK_DETAILS"); + return; + } + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_filename (cfg, + "benchmark", + "merchant_details", + &merchant_details_filename)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "benchmark", + "merchant_details"); + fail ("Failed to get MERCHANT_DETAILS value"); return; } merchant_details = json_load_file (merchant_details_filename, JSON_REJECT_DUPLICATES, NULL); + GNUNET_free (merchant_details_filename); + if (NULL == merchant_details) + { + fail ("Failed to parse file with MERCHANT_DETAILS"); + return; + } - GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_array_append (spent_coins, - spent_coins_size, - 1); - spent_coins_size++; - reserves = NULL; - coins = NULL; + priv = GNUNET_CRYPTO_eddsa_key_create (); + merchant_priv.eddsa_priv = *priv; + GNUNET_free (priv); + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &blinding_key, + sizeof (blinding_key)); + + nreserves = pool_size / COINS_PER_RESERVE; + if (COINS_PER_RESERVE * nreserves < pool_size) + nreserves++; + reserves = GNUNET_new_array (nreserves, + struct Reserve); + ncoins = COINS_PER_RESERVE * nreserves; + coins = GNUNET_new_array (ncoins, + struct Coin); + for (i=0;i < nreserves;i++) + { + struct Reserve *r = &reserves[i]; + + r->reserve_index = i; + GNUNET_CONTAINER_DLL_insert (empty_reserve_head, + empty_reserve_tail, + r); + for (j=0; j < COINS_PER_RESERVE; j++) + { + struct Coin *coin; + unsigned int coin_index; + + coin_index = i * COINS_PER_RESERVE + j; + coin = &coins[coin_index]; + coin->coin_index = coin_index; + coin->reserve_index = i; + invalidate_coin (coin); + } + } + ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, &rc); GNUNET_assert (NULL != ctx); rc = GNUNET_CURL_gnunet_rc_create (ctx); + GNUNET_assert (NULL != rc); exchange = TALER_EXCHANGE_connect (ctx, - EXCHANGE_URI, + exchange_uri, &cert_cb, NULL, TALER_EXCHANGE_OPTION_END); - GNUNET_assert (NULL != exchange); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "connected to exchange\n"); - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + if (NULL == exchange) + { + fail ("Failed to connect to the exchange!"); + return; + } } @@ -1110,78 +1554,111 @@ int main (int argc, char * const *argv) { - - #ifdef RUNXCG struct GNUNET_OS_Process *proc; unsigned int cnt; - #endif - - GNUNET_log_setup ("taler-exchange-benchmark", - "WARNING", - NULL); const struct GNUNET_GETOPT_CommandLineOption options[] = { - {'s', "pool-size", NULL, + {'a', "automate", NULL, + "Initialize and start the bank and exchange", GNUNET_NO, + &GNUNET_GETOPT_set_one, &run_exchange}, + GNUNET_GETOPT_OPTION_CFG_FILE (&config_file), + {'e', "exchange-uri", "URI", + "URI of the exchange", GNUNET_YES, + &GNUNET_GETOPT_set_string, &exchange_uri}, + {'E', "exchange-admin-uri", "URI", + "URI of the administrative interface of the exchange", GNUNET_YES, + &GNUNET_GETOPT_set_string, &exchange_admin_uri}, + GNUNET_GETOPT_OPTION_HELP ("tool to benchmark the Taler exchange"), + {'s', "pool-size", "SIZE", "How many coins this benchmark should instantiate", GNUNET_YES, &GNUNET_GETOPT_set_uint, &pool_size}, - {'c', "config", NULL, - "Configuration file", GNUNET_YES, - &GNUNET_GETOPT_set_string, &config_file} - }; - - GNUNET_assert (GNUNET_SYSERR != - GNUNET_GETOPT_run ("taler-exchange-benchmark", - options, argc, argv)); - #ifdef RUNXCG - proc = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-keyup", - "taler-exchange-keyup", - NULL); - if (NULL == proc) - { - fprintf (stderr, - "Failed to run taler-exchange-keyup. Check your PATH.\n"); - return 77; - } - - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - - proc = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-dbinit", - "taler-exchange-dbinit", - "-r", - NULL); + {'l', "limit", "LIMIT", + "Terminate the benchmark after LIMIT operations", GNUNET_YES, + &GNUNET_GETOPT_set_uint, &num_iterations}, + GNUNET_GETOPT_OPTION_VERBOSE (&be_verbose), + GNUNET_GETOPT_OPTION_END + }; + int ret; - - if (NULL == proc) + GNUNET_log_setup ("taler-exchange-benchmark", + "WARNING", + NULL); + GNUNET_assert (INVALID_COIN_SLACK >= REFRESH_SLOTS_NEEDED); + GNUNET_assert (COIN_VALUE <= (1LL << REFRESH_SLOTS_NEEDED)); + ret = GNUNET_GETOPT_run ("taler-exchange-benchmark", + options, argc, argv); + GNUNET_assert (GNUNET_SYSERR != ret); + if (GNUNET_NO == ret) + return 0; + if ( (0 != num_iterations) && + (WARM_THRESHOLD >= num_iterations) ) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Number of iterations below WARM_THRESHOLD of %llu\n", + WARM_THRESHOLD); + if ( (NULL == exchange_uri) || + (0 == strlen (exchange_uri) )) { - fprintf (stderr, - "Failed to run taler-exchange-dbinit. Check your PATH.\n"); - return 77; + GNUNET_free_non_null (exchange_uri); + exchange_uri = GNUNET_strdup ("http://localhost:8081/"); } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - - exchanged = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-httpd", - "taler-exchange-httpd", - NULL); - if (NULL == exchanged) + if (NULL == exchange_admin_uri) + exchange_admin_uri = GNUNET_strdup ("http://localhost:18080/"); + if (run_exchange) { - fprintf (stderr, - "Failed to run taler-exchange-httpd. Check your PATH.\n"); - return 77; - } - - cnt = 0; - do + char *wget; + + proc = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-exchange-keyup", + "taler-exchange-keyup", + "-c", config_file, + NULL); + if (NULL == proc) + { + fprintf (stderr, + "Failed to run taler-exchange-keyup. Check your PATH.\n"); + return 77; + } + GNUNET_OS_process_wait (proc); + GNUNET_OS_process_destroy (proc); + + proc = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-exchange-dbinit", + "taler-exchange-dbinit", + "-r", + "-c", config_file, + NULL); + if (NULL == proc) { + fprintf (stderr, + "Failed to run taler-exchange-dbinit. Check your PATH.\n"); + return 77; + } + GNUNET_OS_process_wait (proc); + GNUNET_OS_process_destroy (proc); + + exchanged = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-exchange-httpd", + "taler-exchange-httpd", + "-c", config_file, + NULL); + if (NULL == exchanged) + { + fprintf (stderr, + "Failed to run taler-exchange-httpd. Check your PATH.\n"); + return 77; + } + + GNUNET_asprintf (&wget, + "wget -q -t 1 -T 1 %s%skeys -o /dev/null -O /dev/null", + exchange_uri, + (exchange_uri[strlen (exchange_uri)-1] == '/') ? "" : "/"); + cnt = 0; + do { fprintf (stderr, "."); sleep (1); cnt++; @@ -1196,15 +1673,19 @@ main (int argc, return 77; } } - while (0 != system ("wget -q -t 1 -T 1 " EXCHANGE_URI "keys -o /dev/null -O /dev/null")); - fprintf (stderr, "\n"); - #endif - + while (0 != system (wget)); + GNUNET_free (wget); + fprintf (stderr, "\n"); + } GNUNET_SCHEDULER_run (&run, NULL); - #ifdef RUNXCG - GNUNET_OS_process_wait (exchanged); - GNUNET_OS_process_destroy (exchanged); - #endif - - return GNUNET_OK; + if (run_exchange) + { + GNUNET_OS_process_kill (exchanged, + SIGTERM); + GNUNET_OS_process_wait (exchanged); + GNUNET_OS_process_destroy (exchanged); + } + return 0; } + +/* end of taler-exchange-benchmark.c */ diff --git a/src/benchmark/taler-exchange-benchmark.conf b/src/benchmark/taler-exchange-benchmark.conf index 3dc2128fb..16a26d8ae 100644 --- a/src/benchmark/taler-exchange-benchmark.conf +++ b/src/benchmark/taler-exchange-benchmark.conf @@ -1,3 +1,91 @@ [benchmark] -SENDER_DETAILS = ~/exchange/src/benchmark/sender_details.json -MERCHANT_DETAILS = ~/exchange/src/benchmark/merchant_details.json +BANK_DETAILS = bank_details.json +MERCHANT_DETAILS = merchant_details.json + +[PATHS] +# Persistant data storage for the testcase +TALER_TEST_HOME = test_benchmark_home/ + +[taler] +CURRENCY = KUDOS + +[exchange] + +# Wire format supported by the exchange +# We use 'test' for testing of the actual +# coin operations, and 'sepa' to test SEPA-specific routines. +WIREFORMAT = test + +# HTTP port the exchange listens to +PORT = 8081 +# How to access our database +DB = postgres + +# Master public key used to sign the exchange's various keys +MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG + +[exchangedb-postgres] +DB_CONN_STR = "postgres:///talercheck" + + +[exchange-wire-outgoing-test] +# What is the main website of the bank? +# (Not used unless the aggregator is run.) +BANK_URI = "http://localhost:8082/" +# From which account at the 'bank' should outgoing wire transfers be made? +BANK_ACCOUNT_NUMBER = 2 + +[exchange-wire-incoming-test] +# This is the response we give out for the /wire request. It provides +# wallets with the bank information for transfers to the exchange. +TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json + + +[coin_kudos_1] +value = KUDOS:1 +duration_overlap = 5 minutes +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = KUDOS:0.00 +fee_deposit = KUDOS:0.00 +fee_refresh = KUDOS:0.00 +fee_refund = KUDOS:0.00 +rsa_keysize = 1024 + +[coin_kudos_2] +value = KUDOS:2 +duration_overlap = 5 minutes +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = KUDOS:0.00 +fee_deposit = KUDOS:0.00 +fee_refresh = KUDOS:0.00 +fee_refund = KUDOS:0.00 +rsa_keysize = 1024 + +[coin_kudos_4] +value = KUDOS:4 +duration_overlap = 5 minutes +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = KUDOS:0.00 +fee_deposit = KUDOS:0.00 +fee_refresh = KUDOS:0.00 +fee_refund = KUDOS:0.00 +rsa_keysize = 1024 + +[coin_kudos_8] +value = KUDOS:8 +duration_overlap = 5 minutes +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = KUDOS:0.00 +fee_deposit = KUDOS:0.00 +fee_refresh = KUDOS:0.00 +fee_refund = KUDOS:0.00 +rsa_keysize = 1024 + diff --git a/src/benchmark/test_benchmark_home/.config/taler/sepa.json b/src/benchmark/test_benchmark_home/.config/taler/sepa.json new file mode 100644 index 000000000..b435ce86b --- /dev/null +++ b/src/benchmark/test_benchmark_home/.config/taler/sepa.json @@ -0,0 +1,9 @@ +{ + "name": "Max Musterman", + "bic": "COBADEFF370", + "type": "sepa", + "sig": "4EVRC2MCJPXQC8MC00831DNWEXMZAP4JQDDE1A7R6KR3MANG24RC1VQ55AX5A2E35S58VW1VSTENFTPHG5MWG9BSN8B8WXSV21KKW20", + "address": "Musterstadt", + "salt": "3KTM1ZRMWGEQPQ254S4R5R4Q8XM0ZYWTCTE01TZ76MVBSQ6RX7A5DR08WXVH1DCHR1R7ACRB7X0EVC2XDW1CBZM9WFSD9TRMZ90BR98", + "iban": "DE89370400440532013000" +}
\ No newline at end of file diff --git a/src/benchmark/test_benchmark_home/.config/taler/test.json b/src/benchmark/test_benchmark_home/.config/taler/test.json new file mode 100644 index 000000000..be5e92c11 --- /dev/null +++ b/src/benchmark/test_benchmark_home/.config/taler/test.json @@ -0,0 +1,8 @@ +{ + "salt": "AZPRFVJ58NM6M7J5CZQPJAH3EW5DYM52AEZ9Y1C1ER3W94QV8D8TQKF6CK8MYQRA9QMSKDQTGZ306ZS9GQ0M6R01CJ20KPP49WFDZK8", + "name": "The exchange", + "account_number": 3, + "bank_uri": "http://localhost:8082/", + "type": "test", + "sig": "RPQXP9S4P8PQP7HEZQNRSZCT0ATNEP8GW0P5TPM34V5RX86FCD670V44R9NETSYDDKB8SZV7TKY9PAJYTY51D3VDWY9XXQ5BPFRXR28" +}
\ No newline at end of file diff --git a/src/benchmark/test_benchmark_home/.local/share/taler/exchange/offline-keys/master.priv b/src/benchmark/test_benchmark_home/.local/share/taler/exchange/offline-keys/master.priv new file mode 100644 index 000000000..394926938 --- /dev/null +++ b/src/benchmark/test_benchmark_home/.local/share/taler/exchange/offline-keys/master.priv @@ -0,0 +1 @@ +p^-33XX!\0qmU_
\ No newline at end of file diff --git a/src/exchange-lib/exchange_api_refresh.c b/src/exchange-lib/exchange_api_refresh.c index 9a9c6b7eb..e32f73e21 100644 --- a/src/exchange-lib/exchange_api_refresh.c +++ b/src/exchange-lib/exchange_api_refresh.c @@ -764,6 +764,7 @@ TALER_EXCHANGE_refresh_prepare (const struct TALER_CoinSpendPrivateKeyP *melt_pr unsigned int i; unsigned int j; struct GNUNET_HashContext *hash_context; + struct TALER_Amount total; /* build up melt data structure */ for (i=0;i<TALER_CNC_KAPPA;i++) @@ -799,10 +800,44 @@ TALER_EXCHANGE_refresh_prepare (const struct TALER_CoinSpendPrivateKeyP *melt_pr md.fresh_coins[i] = GNUNET_new_array (fresh_pks_len, struct FreshCoinP); for (j=0;j<fresh_pks_len;j++) + { setup_fresh_coin (&md.fresh_coins[i][j], &fresh_pks[j]); + } } + /* verify that melt_amount is above total cost */ + GNUNET_assert (GNUNET_OK == + TALER_amount_get_zero (melt_amount->currency, + &total)); + for (j=0;j<fresh_pks_len;j++) + { + if ( (GNUNET_OK != + TALER_amount_add (&total, + &total, + &fresh_pks[j].value)) || + (GNUNET_OK != + TALER_amount_add (&total, + &total, + &fresh_pks[j].fee_withdraw)) ) + { + GNUNET_break (0); + free_melt_data (&md); + return NULL; + } + } + if (1 == + TALER_amount_cmp (&total, + melt_amount) ) + { + /* Eh, this operation is more expensive than the + @a melt_amount. This is not OK. */ + GNUNET_break (0); + free_melt_data (&md); + return NULL; + } + + /* now compute melt session hash */ hash_context = GNUNET_CRYPTO_hash_context_start (); for (i=0;i<fresh_pks_len;i++) diff --git a/src/exchange-lib/exchange_api_refund.c b/src/exchange-lib/exchange_api_refund.c index fff03acf9..26b960703 100644 --- a/src/exchange-lib/exchange_api_refund.c +++ b/src/exchange-lib/exchange_api_refund.c @@ -283,7 +283,8 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange, "merchant_pub", GNUNET_JSON_from_data_auto (&rr.merchant), "merchant_sig", GNUNET_JSON_from_data_auto (&merchant_sig) ); - + GNUNET_assert (NULL != refund_obj); + rh = GNUNET_new (struct TALER_EXCHANGE_RefundHandle); rh->exchange = exchange; rh->cb = cb; diff --git a/src/exchange-lib/test_exchange_api.conf b/src/exchange-lib/test_exchange_api.conf index a8c690786..03dd6f992 100644 --- a/src/exchange-lib/test_exchange_api.conf +++ b/src/exchange-lib/test_exchange_api.conf @@ -43,7 +43,7 @@ TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json [exchange-wire-outgoing-test] # What is the main website of the bank? BANK_URI = "http://localhost:8082/" -# Into which account at the 'bank' should (incoming) wire transfers be made? +# From which account at the 'bank' should outgoing wire transfers be made? BANK_ACCOUNT_NUMBER = 2 [coin_eur_ct_1] diff --git a/src/exchange-tools/taler-exchange-keyup.c b/src/exchange-tools/taler-exchange-keyup.c index 69d28361f..21024ffca 100644 --- a/src/exchange-tools/taler-exchange-keyup.c +++ b/src/exchange-tools/taler-exchange-keyup.c @@ -807,13 +807,14 @@ exchange_keys_update_cointype (void *cls, &denomkey_issue); if (GNUNET_OK != TALER_EXCHANGEDB_denomination_key_write (dkf, - &denomkey_issue)) + &denomkey_issue)) { fprintf (stderr, "Failed to write denomination key information to file `%s'.\n", dkf); *ret = GNUNET_SYSERR; GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key); + GNUNET_CRYPTO_rsa_public_key_free (denomkey_issue.denom_pub.rsa_public_key); return; } if ( (NULL != auditor_output_file) && @@ -828,9 +829,12 @@ exchange_keys_update_cointype (void *cls, auditorrequestfile, STRERROR (errno)); *ret = GNUNET_SYSERR; + GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key); + GNUNET_CRYPTO_rsa_public_key_free (denomkey_issue.denom_pub.rsa_public_key); return; } GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key); + GNUNET_CRYPTO_rsa_public_key_free (denomkey_issue.denom_pub.rsa_public_key); p.anchor = GNUNET_TIME_absolute_add (p.anchor, p.duration_spend); p.anchor = GNUNET_TIME_absolute_subtract (p.anchor, |