summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--doc/paper/taler.tex37
-rw-r--r--src/bank-lib/Makefile.am1
-rw-r--r--src/benchmark/Makefile.am8
-rw-r--r--src/benchmark/bank_details.json1
-rw-r--r--src/benchmark/merchant_details.json2
-rw-r--r--src/benchmark/sender_details.json1
-rw-r--r--src/benchmark/taler-exchange-benchmark.c1643
-rw-r--r--src/benchmark/taler-exchange-benchmark.conf92
-rw-r--r--src/benchmark/test_benchmark_home/.config/taler/sepa.json9
-rw-r--r--src/benchmark/test_benchmark_home/.config/taler/test.json8
-rw-r--r--src/benchmark/test_benchmark_home/.local/share/taler/exchange/offline-keys/master.priv1
-rw-r--r--src/exchange-lib/exchange_api_refresh.c35
-rw-r--r--src/exchange-lib/exchange_api_refund.c3
-rw-r--r--src/exchange-lib/test_exchange_api.conf2
-rw-r--r--src/exchange-tools/taler-exchange-keyup.c6
16 files changed, 1251 insertions, 599 deletions
diff --git a/.gitignore b/.gitignore
index a928bb4bc..ace3f1375 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,6 +44,7 @@ src/exchange-tools/taler-exchange-reservemod
src/exchange-tools/taler-exchange-wire
src/exchangedb/perf-exchangedb
src/benchmark/taler-exchange-benchmark
+src/benchmark/test_benchmark_home/.local/share/taler/exchange/live-keys/
src/json/test_json
src/wire/test_sepa_wireformat
src/wire/test_wire_plugin
diff --git a/doc/paper/taler.tex b/doc/paper/taler.tex
index a5a1a0354..d93cbf9cc 100644
--- a/doc/paper/taler.tex
+++ b/doc/paper/taler.tex
@@ -583,6 +583,11 @@ protocol messages; denomination keys are used for blind-signing coins.
The exchange's long-term offline key is assumed to be known to both
customers and merchants and is certified by the auditors.
+We avoid asking either customers or merchants to make trust desissions
+about individual exchanges. Instead, they need only select the auditors.
+Auditors must sign all the exchange's keys including, the individual
+denomination keys.
+
As we are dealing with financial transactions, we explicitly describe
whenever entities need to safely commit data to persistent storage.
As long as those commitments persist, the protocol can be safely
@@ -597,14 +602,20 @@ Merchants may discard information once payments from the exchange have
been received, assuming the records are also no longer needed for tax
purposes. The exchange's bank transfers dealing in traditional currency
are expected to be recorded for tax authorities to ensure taxability.
+% FIXME: Auditor?
+
+We use RSA for denomination keys and EdDSA over some eliptic curve
+$\mathbb{E}$ for all other keys. Let $G$ denote the generator of
+our elliptic curve $\mathbb{E}$.
+
\subsection{Withdrawal}
-Let $G$ be the generator of an elliptic curve. To withdraw anonymous
-digital coins, the customer first identifies a exchange with a
-denomination public-private key pair $K := (K_s, K_p)$ corresponding
-to a denomination the customer would like to withdraw, and then
-performs the following interaction with the exchange:
+To withdraw anonymous digital coins, the customer first selects an
+exchange and one of its public denomination public keys $K_p$ whose
+value $K_v$ corresponds to an amount the customer wishes to withdraw.
+We let $K_s$ denote the exchange's private key corresponding to $K_p$.
+Now the customer carries out the following interaction with the exchange:
% FIXME: We say withdrawal key in this document, but say reserve key in
% others, so probably withdrawal key should be renamed to reserve key.
@@ -621,7 +632,7 @@ performs the following interaction with the exchange:
\item coin key $C := (c_s,C_p)$ with private key $c_s$ and public key $C_p := c_s G$,
\item blinding factor $b$, and commits $\langle W, C, b \rangle$ to disk.
\end{itemize}
- \item The customer transfers an amount of money corresponding to at least $K_p$ to the exchange, with $W_p$ in the subject line of the transaction.
+ \item The customer transfers an amount of money corresponding to at least $K_v$ to the exchange, with $W_p$ in the subject line of the transaction.
\item The exchange receives the transaction and credits the $W_p$ reserve with the respective amount in its database.
\item The customer sends $S_W(B_b(C_p))$ to the exchange to request withdrawal of $C$; here, $B_b$ denotes Chaum-style blinding with blinding factor $b$.
\item The exchange checks if the same withdrawal request was issued before; in this case, it sends $S_{K}(B_b(C_p))$ to the customer.\footnote{$S_K$
@@ -636,6 +647,7 @@ performs the following interaction with the exchange:
If the guards for the transaction fail, the exchange sends a descriptive error back to the customer,
with proof that it operated correctly.
Assuming the signature was valid, this would involve showing the transaction history for the reserve.
+ % FIXME: Is it really the whole history?
\item The customer computes and verifies the unblinded signature $S_K(C_p) = U_b(S_K(B_b(C_p)))$.
The customer saves the coin $\langle S_K(C_p), c_s \rangle$ to local wallet on disk.
\end{enumerate}
@@ -644,9 +656,12 @@ performs the following interaction with the exchange:
\subsection{Exact and partial spending}
A customer can spend coins at a merchant, under the condition that the
-merchant trusts the specific exchange that issued the coin. Merchants are
-identified by their key $M := (m_s, M_p)$ where the public key $M_p$
-must be known to the customer a priori.
+merchant trusts the exchange that issued the coin.
+% FIXME: Auditor here?
+Merchants are identified by their public key $M_p = m_s G$ which the
+customer's wallet learns through the merchant's webpage, which itself
+must be authenticated with X.509c.
+% FIXME: Is this correct?
We now describe the protocol between the customer, merchant, and exchange
for a transaction in which the customer spends a coin $C := (c_s, C_p)$
@@ -676,8 +691,8 @@ with signature $\widetilde{C} := S_K(C_p)$
S_c(\widetilde{C}, m, f, H(a), H(p,r), M_p)$
and sends $\langle \mathcal{D}, D_j\rangle$ to the merchant,
where $D_j$ is the exchange which signed $K$.
-\item The merchant gives $(\mathcal{D}, p, r)$ to the exchange, revealing $p$
- only to the exchange.
+\item The merchant gives $(\mathcal{D}, p, r)$ to the exchange, thereby
+ revealing $p$ only to the exchange.
\item The exchange validates $\mathcal{D}$ and checks for double spending.
If the coin has been involved in previous transactions and the new
one would exceed its remaining value, it sends an error
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 (&timestamp);
+ 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 (&timestamp);
- 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,