diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/bank-lib/Makefile.am | 1 | ||||
| -rw-r--r-- | src/bank-lib/fakebank.c | 1112 | ||||
| -rw-r--r-- | src/bank-lib/taler-fakebank-run.c | 12 | ||||
| -rw-r--r-- | 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,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 | 
