gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-exchange] branch master updated: make libfakebank scale


From: gnunet
Subject: [taler-exchange] branch master updated: make libfakebank scale
Date: Sat, 19 Jun 2021 08:36:19 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new 50749c46 make libfakebank scale
50749c46 is described below

commit 50749c462b55b05d42ca518572f75101401089d5
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sat Jun 19 08:36:17 2021 +0200

    make libfakebank scale
---
 src/bank-lib/Makefile.am          |    1 +
 src/bank-lib/fakebank.c           | 1112 +++++++++++++++++++++++++------------
 src/bank-lib/taler-fakebank-run.c |   12 +
 src/include/taler_fakebank_lib.h  |   11 +
 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,20 +132,36 @@ 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?
    */
@@ -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,10 +278,40 @@ 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.
    */
@@ -237,6 +346,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.
  *
@@ -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

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]