summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2021-06-19 08:36:17 +0200
committerChristian Grothoff <christian@grothoff.org>2021-06-19 08:36:17 +0200
commit50749c462b55b05d42ca518572f75101401089d5 (patch)
treec53fd7f066797ccf33c40f553e9526ddc2ecddd2
parent036e7117798246b04fb5d8cbf274abf3b04ddbd3 (diff)
downloadexchange-50749c462b55b05d42ca518572f75101401089d5.tar.gz
exchange-50749c462b55b05d42ca518572f75101401089d5.tar.bz2
exchange-50749c462b55b05d42ca518572f75101401089d5.zip
make libfakebank scale
-rw-r--r--src/bank-lib/Makefile.am1
-rw-r--r--src/bank-lib/fakebank.c1112
-rw-r--r--src/bank-lib/taler-fakebank-run.c12
-rw-r--r--src/include/taler_fakebank_lib.h11
4 files changed, 779 insertions, 357 deletions
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
index 5766cdfe..a520b24d 100644
--- a/src/bank-lib/Makefile.am
+++ b/src/bank-lib/Makefile.am
@@ -67,4 +67,5 @@ libtalerfakebank_la_LIBADD = \
-ljansson \
$(LIBGNURLCURL_LIBS) \
-lmicrohttpd \
+ -lpthread \
$(XLIB)
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index 51a324d4..e091adf5 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -21,7 +21,13 @@
* @brief library that fakes being a Taler bank for testcases
* @author Christian Grothoff <christian@grothoff.org>
*/
+// TOOD: pass test suite...
+// TODO: support long polling
+// TODO: support adding WAD transfers
+// TODO: adapt taler-exchange-benchmark to profile bank API
+
#include "platform.h"
+#include <pthread.h>
#include "taler_fakebank_lib.h"
#include "taler_bank_service.h"
#include "taler_mhd_lib.h"
@@ -32,6 +38,68 @@
*/
#define REQUEST_BUFFER_MAX (4 * 1024)
+/**
+ * How long are exchange base URLs allowed to be at most?
+ * Set to a relatively low number as this does contribute
+ * significantly to our RAM consumption.
+ */
+#define MAX_URL_LEN 64
+
+/**
+ * Details about a transcation we (as the simulated bank) received.
+ */
+struct Transaction;
+
+/**
+ * Per account information.
+ */
+struct Account
+{
+
+ /**
+ * Inbound transactions for this account in a MDLL.
+ */
+ struct Transaction *in_head;
+
+ /**
+ * Inbound transactions for this account in a MDLL.
+ */
+ struct Transaction *in_tail;
+
+ /**
+ * Outbound transactions for this account in a MDLL.
+ */
+ struct Transaction *out_head;
+
+ /**
+ * Outbound transactions for this account in a MDLL.
+ */
+ struct Transaction *out_tail;
+
+ /**
+ * Account name (string, not payto!)
+ */
+ char *account_name;
+
+ /**
+ * Lock for modifying transaction list of this account.
+ * Note that per-transaction locks MUST be acquired before
+ * per-account locks (if both are acquired).
+ */
+ pthread_mutex_t lock;
+
+ /**
+ * Current account balance.
+ */
+ struct TALER_Amount balance;
+
+ /**
+ * true if the balance is negative.
+ */
+ bool is_negative;
+
+};
+
/**
* Details about a transcation we (as the simulated bank) received.
@@ -39,14 +107,24 @@
struct Transaction
{
/**
- * We store transactions in a DLL.
+ * We store inbound transactions in a MDLL.
+ */
+ struct Transaction *next_in;
+
+ /**
+ * We store inbound transactions in a MDLL.
+ */
+ struct Transaction *prev_in;
+
+ /**
+ * We store outbound transactions in a MDLL.
*/
- struct Transaction *next;
+ struct Transaction *next_out;
/**
- * We store transactions in a DLL.
+ * We store outbound transactions in a MDLL.
*/
- struct Transaction *prev;
+ struct Transaction *prev_out;
/**
* Amount to be transferred.
@@ -54,21 +132,37 @@ struct Transaction
struct TALER_Amount amount;
/**
- * Account to debit (string, not payto!)
+ * Account to debit.
*/
- char *debit_account;
+ struct Account *debit_account;
/**
- * Account to credit (string, not payto!)
+ * Account to credit.
*/
- char *credit_account;
+ struct Account *credit_account;
/**
* Random unique identifier for the request.
+ * Used to detect idempotent requests.
*/
struct GNUNET_HashCode request_uid;
/**
+ * When did the transaction happen?
+ */
+ struct GNUNET_TIME_Absolute date;
+
+ /**
+ * Number of this transaction.
+ */
+ uint64_t row_id;
+
+ /**
+ * Lock for accessing this transaction array entry.
+ */
+ pthread_mutex_t lock;
+
+ /**
* What does the @e subject contain?
*/
enum
@@ -109,7 +203,7 @@ struct Transaction
/**
* Base URL of the exchange.
*/
- char *exchange_base_url;
+ char exchange_base_url[MAX_URL_LEN];
} debit;
@@ -140,29 +234,18 @@ struct Transaction
/**
* Base URL of the originating exchange.
*/
- char *origin_base_url;
+ char origin_base_url[MAX_URL_LEN];
} wad;
} subject;
/**
- * When did the transaction happen?
+ * Has this transaction not yet been subjected to
+ * #TALER_FAKEBANK_check_credit() or #TALER_FAKEBANK_check_debit() and
+ * should thus be counted in #TALER_FAKEBANK_check_empty()?
*/
- struct GNUNET_TIME_Absolute date;
-
- /**
- * Number of this transaction.
- */
- uint64_t row_id;
-
- /**
- * Has this transaction been subjected to #TALER_FAKEBANK_check_credit()
- * or #TALER_FAKEBANK_check_debit()
- * and should thus no longer be counted in
- * #TALER_FAKEBANK_check_empty()?
- */
- int checked;
+ bool unchecked;
};
@@ -172,14 +255,9 @@ struct Transaction
struct TALER_FAKEBANK_Handle
{
/**
- * We store transactions in a DLL.
- */
- struct Transaction *transactions_head;
-
- /**
- * We store transactions in a DLL.
+ * We store transactions in a revolving array.
*/
- struct Transaction *transactions_tail;
+ struct Transaction *transactions;
/**
* HTTP server we run to pretend to be the "test" bank.
@@ -187,7 +265,8 @@ struct TALER_FAKEBANK_Handle
struct MHD_Daemon *mhd_bank;
/**
- * Task running HTTP server for the "test" bank.
+ * Task running HTTP server for the "test" bank,
+ * unless we are using a thread pool (then NULL).
*/
struct GNUNET_SCHEDULER_Task *mhd_task;
@@ -199,11 +278,41 @@ struct TALER_FAKEBANK_Handle
struct GNUNET_CONTAINER_MultiPeerMap *rpubs;
/**
- * Number of transactions.
+ * Lock for accessing @a rpubs map.
+ */
+ pthread_mutex_t rpubs_lock;
+
+ /**
+ * Hashmap of hashes of account names to `struct Account`.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *accounts;
+
+ /**
+ * Lock for accessing @a accounts hash map.
+ */
+ pthread_mutex_t accounts_lock;
+
+ /**
+ * Hashmap of hashes of transaction request_uids to `struct Transaction`.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *uuid_map;
+
+ /**
+ * Lock for accessing @a uuid_map.
+ */
+ pthread_mutex_t uuid_map_lock;
+
+ /**
+ * Current transaction counter.
*/
uint64_t serial_counter;
/**
+ * Number of transactions we keep in memory (at most).
+ */
+ uint64_t ram_limit;
+
+ /**
* Currency used by the fakebank.
*/
char *currency;
@@ -238,6 +347,59 @@ struct TALER_FAKEBANK_Handle
/**
+ * Lookup account with @a name, and if it does not exist, create it.
+ *
+ * @param[in,out] bank to lookup account at
+ * @param name account name to resolve
+ * @return account handle (never NULL)
+ */
+static struct Account *
+lookup_account (struct TALER_FAKEBANK_Handle *h,
+ const char *name)
+{
+ struct GNUNET_HashCode hc;
+ size_t slen;
+ struct Account *account;
+
+ memset (&hc,
+ 0,
+ sizeof (hc));
+ slen = strlen (name);
+ if (slen < sizeof (hc))
+ memcpy (&hc,
+ name,
+ slen); /* fake hashing for speed! */
+ else
+ GNUNET_CRYPTO_hash (name,
+ strlen (name),
+ &hc);
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->accounts_lock));
+ account = GNUNET_CONTAINER_multihashmap_get (h->accounts,
+ &hc);
+ if (NULL == account)
+ {
+ account = GNUNET_new (struct Account);
+ GNUNET_assert (0 ==
+ pthread_mutex_init (&account->lock,
+ NULL));
+ account->account_name = GNUNET_strdup (name);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_get_zero (h->currency,
+ &account->balance));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (h->accounts,
+ &hc,
+ account,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->accounts_lock));
+ return account;
+}
+
+
+/**
* Generate log messages for failed check operation.
*
* @param h handle to output transaction log for
@@ -245,19 +407,19 @@ struct TALER_FAKEBANK_Handle
static void
check_log (struct TALER_FAKEBANK_Handle *h)
{
- for (struct Transaction *t = h->transactions_head;
- NULL != t;
- t = t->next)
+ for (uint64_t i = 0; i<h->ram_limit; i++)
{
- if (GNUNET_YES == t->checked)
+ struct Transaction *t = &h->transactions[i];
+
+ if (t->unchecked)
continue;
switch (t->type)
{
case T_DEBIT:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"%s -> %s (%s) %s (%s)\n",
- t->debit_account,
- t->credit_account,
+ t->debit_account->account_name,
+ t->credit_account->account_name,
TALER_amount2s (&t->amount),
t->subject.debit.exchange_base_url,
"DEBIT");
@@ -265,8 +427,8 @@ check_log (struct TALER_FAKEBANK_Handle *h)
case T_CREDIT:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"%s -> %s (%s) %s (%s)\n",
- t->debit_account,
- t->credit_account,
+ t->debit_account->account_name,
+ t->credit_account->account_name,
TALER_amount2s (&t->amount),
TALER_B2S (&t->subject.credit.reserve_pub),
"CREDIT");
@@ -274,8 +436,8 @@ check_log (struct TALER_FAKEBANK_Handle *h)
case T_WAD:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"%s -> %s (%s) %s[%s] (%s)\n",
- t->debit_account,
- t->credit_account,
+ t->debit_account->account_name,
+ t->credit_account->account_name,
TALER_amount2s (&t->amount),
t->subject.wad.origin_base_url,
TALER_B2S (&t->subject.wad),
@@ -294,25 +456,30 @@ TALER_FAKEBANK_check_debit (struct TALER_FAKEBANK_Handle *h,
const char *exchange_base_url,
struct TALER_WireTransferIdentifierRawP *wtid)
{
- GNUNET_assert (0 == strcasecmp (want_amount->currency,
- h->currency));
- for (struct Transaction *t = h->transactions_head;
+ struct Account *debit_account;
+ struct Account *credit_account;
+
+ GNUNET_assert (0 ==
+ strcasecmp (want_amount->currency,
+ h->currency));
+ debit_account = lookup_account (h,
+ want_debit);
+ credit_account = lookup_account (h,
+ want_credit);
+ for (struct Transaction *t = debit_account->out_tail;
NULL != t;
- t = t->next)
+ t = t->prev_out)
{
- if ( (0 == strcasecmp (want_debit,
- t->debit_account)) &&
- (0 == strcasecmp (want_credit,
- t->credit_account)) &&
+ if ( (t->unchecked) &&
+ (credit_account == t->credit_account) &&
+ (T_DEBIT == t->type) &&
(0 == TALER_amount_cmp (want_amount,
&t->amount)) &&
- (GNUNET_NO == t->checked) &&
- (T_DEBIT == t->type) &&
(0 == strcasecmp (exchange_base_url,
t->subject.debit.exchange_base_url)) )
{
*wtid = t->subject.debit.wtid;
- t->checked = GNUNET_YES;
+ t->unchecked = false;
return GNUNET_OK;
}
}
@@ -336,24 +503,28 @@ TALER_FAKEBANK_check_credit (struct TALER_FAKEBANK_Handle *h,
const char *want_credit,
const struct TALER_ReservePublicKeyP *reserve_pub)
{
+ struct Account *debit_account;
+ struct Account *credit_account;
+
GNUNET_assert (0 == strcasecmp (want_amount->currency,
h->currency));
- for (struct Transaction *t = h->transactions_head;
+ debit_account = lookup_account (h,
+ want_debit);
+ credit_account = lookup_account (h,
+ want_credit);
+ for (struct Transaction *t = credit_account->in_tail;
NULL != t;
- t = t->next)
+ t = t->prev_in)
{
- if ( (0 == strcasecmp (want_debit,
- t->debit_account)) &&
- (0 == strcasecmp (want_credit,
- t->credit_account)) &&
+ if ( (t->unchecked) &&
+ (debit_account == t->debit_account) &&
+ (T_CREDIT == t->type) &&
(0 == TALER_amount_cmp (want_amount,
&t->amount)) &&
- (GNUNET_NO == t->checked) &&
- (T_CREDIT == t->type) &&
(0 == GNUNET_memcmp (reserve_pub,
&t->subject.credit.reserve_pub)) )
{
- t->checked = GNUNET_YES;
+ t->unchecked = false;
return GNUNET_OK;
}
}
@@ -370,6 +541,137 @@ TALER_FAKEBANK_check_credit (struct TALER_FAKEBANK_Handle *h,
}
+/**
+ * Clean up space used by old transaction @a t.
+ * The transaction @a t must already be locked
+ * when calling this function!
+ *
+ * @param[in,out] h bank handle
+ * @param[in] t transaction to clean up
+ */
+static void
+clean_transaction (struct TALER_FAKEBANK_Handle *h,
+ struct Transaction *t)
+{
+ struct Account *da = t->debit_account;
+ struct Account *ca = t->credit_account;
+
+ if (NULL == da)
+ return; /* nothing to be cleaned */
+
+ /* slot was already in use, must clean out old
+ entry first! */
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&da->lock));
+ GNUNET_CONTAINER_MDLL_remove (out,
+ da->out_head,
+ da->out_tail,
+ t);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&da->lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&ca->lock));
+ GNUNET_CONTAINER_MDLL_remove (in,
+ ca->in_head,
+ ca->in_tail,
+ t);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&ca->lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->uuid_map_lock));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_remove (h->uuid_map,
+ &t->request_uid,
+ t));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ t->debit_account = NULL;
+ t->credit_account = NULL;
+}
+
+
+/**
+ * Update @a account balance by @a amount.
+ *
+ * The @a account must already be locked when calling
+ * this function.
+ *
+ * @param[in,out] account account to update
+ * @param amount balance change
+ * @param debit true to subtract, false to add @a amount
+ */
+static void
+update_balance (struct Account *account,
+ const struct TALER_Amount *amount,
+ bool debit)
+{
+ if (debit == account->is_negative)
+ {
+ GNUNET_assert (0 <=
+ TALER_amount_add (&account->balance,
+ &account->balance,
+ amount));
+ return;
+ }
+ if (0 <= TALER_amount_cmp (&account->balance,
+ amount))
+ {
+ GNUNET_assert (0 <=
+ TALER_amount_subtract (&account->balance,
+ &account->balance,
+ amount));
+ }
+ else
+ {
+ GNUNET_assert (0 <=
+ TALER_amount_subtract (&account->balance,
+ amount,
+ &account->balance));
+ account->is_negative = ! account->is_negative;
+ }
+}
+
+
+/**
+ * Add transaction to the debit and credit accounts,
+ * updating the balances as needed.
+ *
+ * The transaction @a t must already be locked
+ * when calling this function!
+ *
+ * @param[in] t transaction to clean up
+ */
+static void
+post_transaction (struct Transaction *t)
+{
+ struct Account *debit_acc = t->debit_account;
+ struct Account *credit_acc = t->credit_account;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&debit_acc->lock));
+ GNUNET_CONTAINER_MDLL_insert_tail (out,
+ debit_acc->out_head,
+ debit_acc->out_tail,
+ t);
+ update_balance (debit_acc,
+ &t->amount,
+ true);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&debit_acc->lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&credit_acc->lock));
+ GNUNET_CONTAINER_MDLL_insert_tail (in,
+ credit_acc->in_head,
+ credit_acc->in_tail,
+ t);
+ update_balance (credit_acc,
+ &t->amount,
+ false);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&credit_acc->lock));
+}
+
+
int
TALER_FAKEBANK_make_transfer (
struct TALER_FAKEBANK_Handle *h,
@@ -382,6 +684,9 @@ TALER_FAKEBANK_make_transfer (
uint64_t *ret_row_id)
{
struct Transaction *t;
+ struct Account *debit_acc;
+ struct Account *credit_acc;
+ size_t url_len;
GNUNET_assert (0 == strcasecmp (amount->currency,
h->currency));
@@ -391,19 +696,26 @@ TALER_FAKEBANK_make_transfer (
GNUNET_break (0 != strncasecmp ("payto://",
credit_account,
strlen ("payto://")));
+ url_len = strlen (exchange_base_url);
+ GNUNET_assert (url_len < MAX_URL_LEN);
+ debit_acc = lookup_account (h,
+ debit_account);
+ credit_acc = lookup_account (h,
+ credit_account);
if (NULL != request_uid)
{
- for (struct Transaction *t = h->transactions_head;
- NULL != t;
- t = t->next)
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->uuid_map_lock));
+ t = GNUNET_CONTAINER_multihashmap_get (h->uuid_map,
+ request_uid);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ if (NULL != t)
{
- if (0 != GNUNET_memcmp (request_uid,
- &t->request_uid))
- continue;
- if ( (0 != strcasecmp (debit_account,
- t->debit_account)) ||
- (0 != strcasecmp (credit_account,
- t->credit_account)) ||
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&t->lock));
+ if ( (debit_acc != t->debit_account) ||
+ (credit_acc != t->credit_account) ||
(0 != TALER_amount_cmp (amount,
&t->amount)) ||
(T_DEBIT != t->type) ||
@@ -412,38 +724,61 @@ TALER_FAKEBANK_make_transfer (
{
/* Transaction exists, but with different details. */
GNUNET_break (0);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&t->lock));
return GNUNET_SYSERR;
}
*ret_row_id = t->row_id;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&t->lock));
return GNUNET_OK;
}
}
+ *ret_row_id = __sync_fetch_and_add (&h->serial_counter,
+ 1);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Making transfer from %s to %s over %s and subject %s; for exchange: %s\n",
+ "Making transfer %llu from %s to %s over %s and subject %s; for exchange: %s\n",
+ (unsigned long long) *ret_row_id,
debit_account,
credit_account,
TALER_amount2s (amount),
TALER_B2S (subject),
exchange_base_url);
- t = GNUNET_new (struct Transaction);
- t->debit_account = GNUNET_strdup (debit_account);
- t->credit_account = GNUNET_strdup (credit_account);
+ t = &h->transactions[*ret_row_id % h->ram_limit];
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&t->lock));
+ clean_transaction (h,
+ t);
+ t->unchecked = true;
+ t->debit_account = debit_acc;
+ t->credit_account = credit_acc;
t->amount = *amount;
- t->row_id = ++h->serial_counter;
+ t->row_id = *ret_row_id;
t->date = GNUNET_TIME_absolute_get ();
+ (void) GNUNET_TIME_round_abs (&t->date);
t->type = T_DEBIT;
- t->subject.debit.exchange_base_url = GNUNET_strdup (exchange_base_url);
+ memcpy (t->subject.debit.exchange_base_url,
+ exchange_base_url,
+ url_len);
t->subject.debit.wtid = *subject;
if (NULL == request_uid)
GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
&t->request_uid);
else
t->request_uid = *request_uid;
- GNUNET_TIME_round_abs (&t->date);
- GNUNET_CONTAINER_DLL_insert_tail (h->transactions_head,
- h->transactions_tail,
- t);
- *ret_row_id = t->row_id;
+ post_transaction (t);
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->uuid_map_lock));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (
+ h->uuid_map,
+ &t->request_uid,
+ t,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&t->lock));
return GNUNET_OK;
}
@@ -458,103 +793,118 @@ TALER_FAKEBANK_make_admin_transfer (
{
struct Transaction *t;
const struct GNUNET_PeerIdentity *pid;
+ struct Account *debit_acc;
+ struct Account *credit_acc;
+ uint64_t ret;
- GNUNET_assert (sizeof (*pid) ==
- sizeof (*reserve_pub));
+ GNUNET_static_assert (sizeof (*pid) ==
+ sizeof (*reserve_pub));
pid = (const struct GNUNET_PeerIdentity *) reserve_pub;
- t = GNUNET_CONTAINER_multipeermap_get (h->rpubs,
- pid);
- if (NULL != t)
- {
- GNUNET_break (0);
- return 0;
- }
- GNUNET_assert (0 == strcasecmp (amount->currency,
- h->currency));
GNUNET_assert (NULL != debit_account);
GNUNET_assert (NULL != credit_account);
+ GNUNET_assert (0 == strcasecmp (amount->currency,
+ h->currency));
GNUNET_break (0 != strncasecmp ("payto://",
debit_account,
strlen ("payto://")));
GNUNET_break (0 != strncasecmp ("payto://",
credit_account,
strlen ("payto://")));
- t = GNUNET_new (struct Transaction);
- t->debit_account = GNUNET_strdup (debit_account);
- t->credit_account = GNUNET_strdup (credit_account);
+ debit_acc = lookup_account (h,
+ debit_account);
+ credit_acc = lookup_account (h,
+ credit_account);
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->rpubs_lock));
+ t = GNUNET_CONTAINER_multipeermap_get (h->rpubs,
+ pid);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->rpubs_lock));
+ if (NULL != t)
+ {
+ /* duplicate reserve public key not allowed */
+ GNUNET_break (0);
+ return 0;
+ }
+
+ ret = __sync_fetch_and_add (&h->serial_counter,
+ 1);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Making transfer from %s to %s over %s and subject %s at row %llu\n",
+ debit_account,
+ credit_account,
+ TALER_amount2s (amount),
+ TALER_B2S (reserve_pub),
+ (unsigned long long) ret);
+ t = &h->transactions[ret % h->ram_limit];
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&t->lock));
+ clean_transaction (h,
+ t);
+ t->unchecked = true;
+ t->debit_account = debit_acc;
+ t->credit_account = credit_acc;
t->amount = *amount;
- t->row_id = ++h->serial_counter;
+ t->row_id = ret;
t->date = GNUNET_TIME_absolute_get ();
+ GNUNET_TIME_round_abs (&t->date);
t->type = T_CREDIT;
t->subject.credit.reserve_pub = *reserve_pub;
- GNUNET_TIME_round_abs (&t->date);
- GNUNET_CONTAINER_DLL_insert_tail (h->transactions_head,
- h->transactions_tail,
- t);
+ post_transaction (t);
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->rpubs_lock));
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multipeermap_put (
h->rpubs,
pid,
t,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Making transfer from %s to %s over %s and subject %s at row %llu\n",
- debit_account,
- credit_account,
- TALER_amount2s (amount),
- TALER_B2S (reserve_pub),
- (unsigned long long) t->row_id);
- return t->row_id;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->rpubs_lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&t->lock));
+ return ret;
}
int
TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h)
{
- struct Transaction *t;
-
- t = h->transactions_head;
- while (NULL != t)
+ for (uint64_t i = 0; i<h->ram_limit; i++)
{
- if (GNUNET_YES != t->checked)
- break;
- t = t->next;
+ struct Transaction *t = &h->transactions[i];
+
+ if (t->unchecked)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected empty transaction set, but I have:\n");
+ check_log (h);
+ return GNUNET_SYSERR;
+ }
}
- if (NULL == t)
- return GNUNET_OK;
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Expected empty transaction set, but I have:\n");
- check_log (h);
- return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+static int
+free_account (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *val)
+{
+ struct Account *account = val;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_destroy (&account->lock));
+ GNUNET_free (account->account_name);
+ GNUNET_free (account);
+ return GNUNET_OK;
}
void
TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
{
- struct Transaction *t;
-
- while (NULL != (t = h->transactions_head))
- {
- GNUNET_CONTAINER_DLL_remove (h->transactions_head,
- h->transactions_tail,
- t);
- GNUNET_free (t->debit_account);
- GNUNET_free (t->credit_account);
- switch (t->type)
- {
- case T_CREDIT:
- /* nothing to free */
- break;
- case T_DEBIT:
- GNUNET_free (t->subject.debit.exchange_base_url);
- break;
- case T_WAD:
- GNUNET_free (t->subject.wad.origin_base_url);
- break;
- }
- GNUNET_free (t);
- }
if (NULL != h->mhd_task)
{
GNUNET_SCHEDULER_cancel (h->mhd_task);
@@ -568,8 +918,25 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
MHD_stop_daemon (h->mhd_bank);
h->mhd_bank = NULL;
}
- GNUNET_free (h->my_baseurl);
+ if (NULL != h->accounts)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (h->accounts,
+ &free_account,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (h->accounts);
+ }
+ GNUNET_CONTAINER_multihashmap_destroy (h->uuid_map);
GNUNET_CONTAINER_multipeermap_destroy (h->rpubs);
+ for (uint64_t i = 0; i<h->ram_limit; i++)
+ pthread_mutex_destroy (&h->transactions[i].lock);
+ GNUNET_assert (0 ==
+ pthread_mutex_destroy (&h->uuid_map_lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_destroy (&h->accounts_lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_destroy (&h->rpubs_lock));
+ GNUNET_free (h->transactions);
+ GNUNET_free (h->my_baseurl);
GNUNET_free (h->currency);
GNUNET_free (h);
}
@@ -677,6 +1044,9 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
if (0 != strcasecmp (amount.currency,
h->currency))
{
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Currency `%s' does not match our configuration\n",
+ amount.currency);
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_CONFLICT,
@@ -698,6 +1068,8 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
GNUNET_free (debit);
if (0 == row_id)
{
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Reserve public key not unique\n");
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_CONFLICT,
@@ -707,6 +1079,8 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
}
json_decref (json);
+ // FIXME: timestamp without lock is unclean,
+ // return as part of TALER_FAKEBANK_make_admin_transfer instead!
/* Finally build response object */
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_OK,
@@ -715,7 +1089,7 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
(json_int_t) row_id,
"timestamp",
GNUNET_JSON_from_time_abs (
- h->transactions_tail->date));
+ h->transactions[row_id].date));
}
@@ -866,10 +1240,10 @@ handle_home_page (struct TALER_FAKEBANK_Handle *h,
(void) h;
(void) con_cls;
- resp = MHD_create_response_from_buffer
- (strlen (HELLOMSG),
- HELLOMSG,
- MHD_RESPMEM_MUST_COPY);
+ resp = MHD_create_response_from_buffer (
+ strlen (HELLOMSG),
+ HELLOMSG,
+ MHD_RESPMEM_MUST_COPY);
ret = MHD_queue_response (connection,
MHD_HTTP_OK,
resp);
@@ -907,9 +1281,9 @@ struct HistoryArgs
struct GNUNET_TIME_Relative lp_timeout;
/**
- * #GNUNET_YES if starting point was given.
+ * true if starting point was given.
*/
- int have_start;
+ bool have_start;
};
@@ -995,8 +1369,10 @@ handle_debit_history (struct TALER_FAKEBANK_Handle *h,
const char *account)
{
struct HistoryArgs ha;
- const struct Transaction *pos;
+ struct Account *acc;
+ struct Transaction *pos;
json_t *history;
+ char *debit_payto;
if (GNUNET_OK !=
parse_history_common_args (connection,
@@ -1006,88 +1382,87 @@ handle_debit_history (struct TALER_FAKEBANK_Handle *h,
return MHD_NO;
}
+ acc = lookup_account (h,
+ account);
if (! ha.have_start)
{
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&acc->lock));
pos = (0 > ha.delta)
- ? h->transactions_tail
- : h->transactions_head;
+ ? acc->out_tail
+ : acc->out_head;
}
- else if (NULL != h->transactions_head)
+ else
{
- for (pos = h->transactions_head;
- NULL != pos;
- pos = pos->next)
- if (pos->row_id == ha.start_idx)
- break;
- if (NULL == pos)
+ struct Transaction *t = &h->transactions[ha.start_idx];
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&t->lock));
+ if (t->debit_account != acc)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid start specified, transaction %llu not known!\n",
- (unsigned long long) ha.start_idx);
+ "Invalid start specified, transaction %llu not with account %s!\n",
+ (unsigned long long) ha.start_idx,
+ account);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&t->lock));
return MHD_NO;
}
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&acc->lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&t->lock));
/* range is exclusive, skip the matching entry */
if (0 > ha.delta)
- pos = pos->prev;
+ pos = t->prev_out;
else
- pos = pos->next;
- }
- else
- {
- /* list is empty */
- pos = NULL;
+ pos = t->next_out;
}
+ GNUNET_asprintf (&debit_payto,
+ "payto://x-taler-bank/localhost/%s",
+ account);
history = json_array ();
while ( (0 != ha.delta) &&
(NULL != pos) )
{
- if ( (0 == strcasecmp (pos->debit_account,
- account)) &&
- (T_DEBIT == pos->type) )
- {
- json_t *trans;
- char *credit_payto;
- char *debit_payto;
-
- GNUNET_asprintf (&credit_payto,
- "payto://x-taler-bank/localhost/%s",
- pos->credit_account);
-
- GNUNET_asprintf (&debit_payto,
- "payto://x-taler-bank/localhost/%s",
- pos->debit_account);
+ json_t *trans;
+ char *credit_payto;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "made credit_payto (%s) from credit_account (%s) within fakebank\n",
- credit_payto,
- pos->credit_account);
-
- trans = json_pack
- ("{s:I, s:o, s:o, s:s, s:s, s:s, s:o}",
- "row_id", (json_int_t) pos->row_id,
- "date", GNUNET_JSON_from_time_abs (pos->date),
- "amount", TALER_JSON_from_amount (&pos->amount),
- "credit_account", credit_payto,
- "debit_account", debit_payto,
- "exchange_base_url",
- pos->subject.debit.exchange_base_url,
- "wtid", GNUNET_JSON_from_data_auto (
- &pos->subject.debit.wtid));
- GNUNET_free (credit_payto);
- GNUNET_free (debit_payto);
- GNUNET_assert (0 ==
- json_array_append_new (history,
- trans));
- if (ha.delta > 0)
- ha.delta--;
- else
- ha.delta++;
- }
+ GNUNET_assert (T_DEBIT == pos->type);
+ GNUNET_asprintf (&credit_payto,
+ "payto://x-taler-bank/localhost/%s",
+ pos->credit_account->account_name);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Appending credit_payto (%s) from credit_account (%s) within fakebank\n",
+ credit_payto,
+ pos->credit_account->account_name);
+ trans = json_pack (
+ "{s:I, s:o, s:o, s:s, s:s, s:s, s:o}",
+ "row_id", (json_int_t) pos->row_id,
+ "date", GNUNET_JSON_from_time_abs (pos->date),
+ "amount", TALER_JSON_from_amount (&pos->amount),
+ "credit_account", credit_payto,
+ "debit_account", debit_payto, // FIXME: inefficient to return this here always!
+ "exchange_base_url",
+ pos->subject.debit.exchange_base_url,
+ "wtid", GNUNET_JSON_from_data_auto (
+ &pos->subject.debit.wtid));
+ GNUNET_free (credit_payto);
+ GNUNET_assert (0 ==
+ json_array_append_new (history,
+ trans));
+ if (ha.delta > 0)
+ ha.delta--;
+ else
+ ha.delta++;
if (0 > ha.delta)
- pos = pos->prev;
+ pos = pos->prev_out;
if (0 < ha.delta)
- pos = pos->next;
+ pos = pos->next_out;
}
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&acc->lock));
+ GNUNET_free (debit_payto);
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o}",
@@ -1110,8 +1485,10 @@ handle_credit_history (struct TALER_FAKEBANK_Handle *h,
const char *account)
{
struct HistoryArgs ha;
+ struct Account *acc;
const struct Transaction *pos;
json_t *history;
+ char *credit_payto;
if (GNUNET_OK !=
parse_history_common_args (connection,
@@ -1120,116 +1497,87 @@ handle_credit_history (struct TALER_FAKEBANK_Handle *h,
GNUNET_break (0);
return MHD_NO;
}
+ acc = lookup_account (h,
+ account);
if (! ha.have_start)
{
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&acc->lock));
pos = (0 > ha.delta)
- ? h->transactions_tail
- : h->transactions_head;
+ ? acc->in_tail
+ : acc->in_head;
}
- else if (NULL != h->transactions_head)
+ else
{
- for (pos = h->transactions_head;
- NULL != pos;
- pos = pos->next)
- {
- if (pos->row_id == ha.start_idx)
- break;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Skipping transaction %s->%s (%s) at %llu (looking for start index %llu)\n",
- pos->debit_account,
- pos->credit_account,
- TALER_B2S (&pos->subject.credit.reserve_pub),
- (unsigned long long) pos->row_id,
- (unsigned long long) ha.start_idx);
- }
- if (NULL == pos)
+ struct Transaction *t = &h->transactions[ha.start_idx];
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&t->lock));
+ if (t->credit_account != acc)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid start specified, transaction %llu not known!\n",
- (unsigned long long) ha.start_idx);
+ "Invalid start specified, transaction %llu not with account %s!\n",
+ (unsigned long long) ha.start_idx,
+ account);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&t->lock));
return MHD_NO;
}
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&acc->lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&t->lock));
/* range is exclusive, skip the matching entry */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Skipping transaction %s->%s (%s) (start index %llu is exclusive)\n",
- pos->debit_account,
- pos->credit_account,
- TALER_B2S (&pos->subject.credit.reserve_pub),
- (unsigned long long) ha.start_idx);
if (0 > ha.delta)
- pos = pos->prev;
+ pos = t->prev_in;
else
- pos = pos->next;
- }
- else
- {
- /* list is empty */
- pos = NULL;
+ pos = t->next_in;
}
+ GNUNET_asprintf (&credit_payto,
+ "payto://x-taler-bank/localhost/%s",
+ account);
history = json_array ();
while ( (0 != ha.delta) &&
(NULL != pos) )
{
- if ( (0 == strcasecmp (pos->credit_account,
- account)) &&
- (T_CREDIT == pos->type) )
- {
- json_t *trans;
- char *credit_payto;
- char *debit_payto;
-
- GNUNET_asprintf (&credit_payto,
- "payto://x-taler-bank/localhost/%s",
- pos->credit_account);
-
- GNUNET_asprintf (&debit_payto,
- "payto://x-taler-bank/localhost/%s",
- pos->debit_account);
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "made credit_payto (%s) from credit_account (%s) within fakebank\n",
- credit_payto,
- pos->credit_account);
+ json_t *trans;
+ char *debit_payto;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Returning transaction %s->%s (%s) at %llu\n",
- pos->debit_account,
- pos->credit_account,
- TALER_B2S (&pos->subject.credit.reserve_pub),
- (unsigned long long) pos->row_id);
- trans = json_pack
- ("{s:I, s:o, s:o, s:s, s:s, s:o}",
- "row_id", (json_int_t) pos->row_id,
- "date", GNUNET_JSON_from_time_abs (pos->date),
- "amount", TALER_JSON_from_amount (&pos->amount),
- "credit_account", credit_payto,
- "debit_account", debit_payto,
- "reserve_pub", GNUNET_JSON_from_data_auto (
- &pos->subject.credit.reserve_pub));
- GNUNET_free (credit_payto);
- GNUNET_free (debit_payto);
- GNUNET_assert (0 ==
- json_array_append_new (history,
- trans));
- if (ha.delta > 0)
- ha.delta--;
- else
- ha.delta++;
- }
- else if (T_CREDIT == pos->type)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Skipping transaction %s->%s (%s) at row %llu\n",
- pos->debit_account,
- pos->credit_account,
- TALER_B2S (&pos->subject.credit.reserve_pub),
- (unsigned long long) pos->row_id);
- }
+ GNUNET_assert (T_CREDIT == pos->type);
+ GNUNET_asprintf (&debit_payto,
+ "payto://x-taler-bank/localhost/%s",
+ pos->debit_account->account_name);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Returning transaction %s->%s (%s) at %llu\n",
+ pos->debit_account->account_name,
+ pos->credit_account->account_name,
+ TALER_B2S (&pos->subject.credit.reserve_pub),
+ (unsigned long long) pos->row_id);
+ trans = json_pack (
+ "{s:I, s:o, s:o, s:s, s:s, s:o}",
+ "row_id", (json_int_t) pos->row_id,
+ "date", GNUNET_JSON_from_time_abs (pos->date),
+ "amount", TALER_JSON_from_amount (&pos->amount),
+ "credit_account", credit_payto, // FIXME: inefficient to repeat this always here!
+ "debit_account", debit_payto,
+ "reserve_pub", GNUNET_JSON_from_data_auto (
+ &pos->subject.credit.reserve_pub));
+ GNUNET_free (debit_payto);
+ GNUNET_assert (0 ==
+ json_array_append_new (history,
+ trans));
+ if (ha.delta > 0)
+ ha.delta--;
+ else
+ ha.delta++;
if (0 > ha.delta)
- pos = pos->prev;
+ pos = pos->prev_in;
if (0 < ha.delta)
- pos = pos->next;
+ pos = pos->next_in;
}
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&acc->lock));
+ GNUNET_free (credit_payto);
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o}",
@@ -1265,51 +1613,48 @@ serve (struct TALER_FAKEBANK_Handle *h,
"Fakebank, serving URL `%s' for account `%s'\n",
url,
account);
- if ( (0 == strcmp (url,
- "/")) &&
- (0 == strcasecmp (method,
- MHD_HTTP_METHOD_GET)) )
- return handle_home_page (h,
- connection,
- con_cls);
- if ( (0 == strcmp (url,
- "/admin/add-incoming")) &&
- (0 == strcasecmp (method,
- MHD_HTTP_METHOD_POST)) )
- return handle_admin_add_incoming (h,
- connection,
- account,
- upload_data,
- upload_data_size,
- con_cls);
- if ( (0 == strcmp (url,
- "/transfer")) &&
- (NULL != account) &&
- (0 == strcasecmp (method,
- MHD_HTTP_METHOD_POST)) )
- return handle_transfer (h,
- connection,
- account,
- upload_data,
- upload_data_size,
- con_cls);
- if ( (0 == strcmp (url,
- "/history/incoming")) &&
- (NULL != account) &&
- (0 == strcasecmp (method,
- MHD_HTTP_METHOD_GET)) )
- return handle_credit_history (h,
- connection,
- account);
- if ( (0 == strcmp (url,
- "/history/outgoing")) &&
- (NULL != account) &&
- (0 == strcasecmp (method,
- MHD_HTTP_METHOD_GET)) )
- return handle_debit_history (h,
- connection,
- account);
-
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET))
+ {
+ if ( (0 == strcmp (url,
+ "/history/incoming")) &&
+ (NULL != account) )
+ return handle_credit_history (h,
+ connection,
+ account);
+ if ( (0 == strcmp (url,
+ "/history/outgoing")) &&
+ (NULL != account) )
+ return handle_debit_history (h,
+ connection,
+ account);
+ if (0 == strcmp (url,
+ "/"))
+ return handle_home_page (h,
+ connection,
+ con_cls);
+ }
+ else if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_POST))
+ {
+ if (0 == strcmp (url,
+ "/admin/add-incoming"))
+ return handle_admin_add_incoming (h,
+ connection,
+ account,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ if ( (0 == strcmp (url,
+ "/transfer")) &&
+ (NULL != account) )
+ return handle_transfer (h,
+ connection,
+ account,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ }
/* Unexpected URL path, just close the connection. */
/* We're rather impolite here, but it's a testcase. */
TALER_LOG_ERROR ("Breaking URL: %s %s\n",
@@ -1509,6 +1854,7 @@ TALER_FAKEBANK_start (uint16_t port,
{
return TALER_FAKEBANK_start2 (port,
currency,
+ 65536, /* RAM limit */
0,
false);
}
@@ -1517,17 +1863,70 @@ TALER_FAKEBANK_start (uint16_t port,
struct TALER_FAKEBANK_Handle *
TALER_FAKEBANK_start2 (uint16_t port,
const char *currency,
+ uint64_t ram_limit,
unsigned int num_threads,
bool close_connections)
{
struct TALER_FAKEBANK_Handle *h;
+ if (SIZE_MAX / sizeof (struct Transaction) < ram_limit)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "This CPU architecture does not support keeping %llu transactions in RAM\n",
+ (unsigned long long) ram_limit);
+ return NULL;
+ }
GNUNET_assert (strlen (currency) < TALER_CURRENCY_LEN);
h = GNUNET_new (struct TALER_FAKEBANK_Handle);
h->port = port;
h->force_close = close_connections;
- h->rpubs = GNUNET_CONTAINER_multipeermap_create (128,
+ h->ram_limit = ram_limit;
+ h->serial_counter = 1;
+ GNUNET_assert (0 ==
+ pthread_mutex_init (&h->accounts_lock,
+ NULL));
+ GNUNET_assert (0 ==
+ pthread_mutex_init (&h->rpubs_lock,
+ NULL));
+ GNUNET_assert (0 ==
+ pthread_mutex_init (&h->uuid_map_lock,
+ NULL));
+ h->transactions
+ = GNUNET_malloc_large (sizeof (struct Transaction)
+ * ram_limit);
+ if (NULL == h->transactions)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "malloc");
+ TALER_FAKEBANK_stop (h);
+ return NULL;
+ }
+ h->accounts = GNUNET_CONTAINER_multihashmap_create (128,
+ GNUNET_NO);
+ h->uuid_map = GNUNET_CONTAINER_multihashmap_create (ram_limit * 4 / 3,
+ GNUNET_YES);
+ if (NULL == h->uuid_map)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "malloc");
+ TALER_FAKEBANK_stop (h);
+ return NULL;
+ }
+ h->rpubs = GNUNET_CONTAINER_multipeermap_create (ram_limit * 4 / 3,
GNUNET_NO);
+ if (NULL == h->rpubs)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "malloc");
+ TALER_FAKEBANK_stop (h);
+ return NULL;
+ }
+ for (uint64_t i = 0; i<ram_limit; i++)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_init (&h->transactions[i].lock,
+ NULL));
+ }
h->currency = GNUNET_strdup (currency);
GNUNET_asprintf (&h->my_baseurl,
"http://localhost:%u/",
@@ -1549,8 +1948,7 @@ TALER_FAKEBANK_start2 (uint16_t port,
MHD_OPTION_END);
if (NULL == h->mhd_bank)
{
- GNUNET_free (h->currency);
- GNUNET_free (h);
+ TALER_FAKEBANK_stop (h);
return NULL;
}
#if EPOLL_SUPPORT
@@ -1580,8 +1978,8 @@ TALER_FAKEBANK_start2 (uint16_t port,
MHD_OPTION_END);
if (NULL == h->mhd_bank)
{
- GNUNET_free (h->currency);
- GNUNET_free (h);
+ GNUNET_break (0);
+ TALER_FAKEBANK_stop (h);
return NULL;
}
}
diff --git a/src/bank-lib/taler-fakebank-run.c b/src/bank-lib/taler-fakebank-run.c
index fc8fcebf..5aa8650a 100644
--- a/src/bank-lib/taler-fakebank-run.c
+++ b/src/bank-lib/taler-fakebank-run.c
@@ -59,6 +59,7 @@ run (void *cls,
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
unsigned long long port = 8082;
+ unsigned long long ram = 1024 * 1024 * 128; /* 128 M entries */
char *currency_string;
(void) cls;
@@ -81,9 +82,20 @@ run (void *cls,
"Listening on default port %llu\n",
port);
}
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg,
+ "bank",
+ "RAM_LIMIT",
+ &ram))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Maximum transaction history in RAM set to default of %llu\n",
+ ram);
+ }
if (NULL ==
TALER_FAKEBANK_start2 ((uint16_t) port,
currency_string,
+ ram,
num_threads,
(0 != connection_close) ))
ret = 1;
diff --git a/src/include/taler_fakebank_lib.h b/src/include/taler_fakebank_lib.h
index 828282c8..60283e7f 100644
--- a/src/include/taler_fakebank_lib.h
+++ b/src/include/taler_fakebank_lib.h
@@ -64,6 +64,7 @@ TALER_FAKEBANK_start (uint16_t port,
*
* @param port port to listen to
* @param currency which currency should the bank offer
+ * @param ram_limit how much memory do we use at most
* @param num_threads size of the thread pool, 0 to use the GNUnet scheduler
* @param close_connections true to force closing a connection after each request (no HTTP keep-alive)
* @return NULL on error
@@ -71,6 +72,7 @@ TALER_FAKEBANK_start (uint16_t port,
struct TALER_FAKEBANK_Handle *
TALER_FAKEBANK_start2 (uint16_t port,
const char *currency,
+ uint64_t ram_limit,
unsigned int num_threads,
bool close_connections);
@@ -81,6 +83,9 @@ TALER_FAKEBANK_start2 (uint16_t port,
* or #TALER_FAKEBANK_check_credit()).
* If any transactions are onrecord, return #GNUNET_SYSERR.
*
+ * Note that this function only works in
+ * single-threaded mode while nothing else is happening.
+ *
* @param h bank instance
* @return #GNUNET_OK on success
*/
@@ -139,6 +144,9 @@ TALER_FAKEBANK_make_admin_transfer (
* to the transfer identifier and remove the transaction from the
* list. If the transaction was not recorded, return #GNUNET_SYSERR.
*
+ * Note that this function only works in
+ * single-threaded mode while nothing else is happening.
+ *
* @param h bank instance
* @param want_amount transfer amount desired
* @param want_debit account that should have been debited
@@ -162,6 +170,9 @@ TALER_FAKEBANK_check_debit (struct TALER_FAKEBANK_Handle *h,
* @a want_credit account with the @a subject. If so, remove the transaction
* from the list. If the transaction was not recorded, return #GNUNET_SYSERR.
*
+ * Note that this function only works in
+ * single-threaded mode while nothing else is happening.
+ *
* @param h bank instance
* @param want_amount transfer amount desired
* @param want_debit account that should have been debited