summaryrefslogtreecommitdiff
path: root/src/bank-lib/fakebank_common_transact.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bank-lib/fakebank_common_transact.c')
-rw-r--r--src/bank-lib/fakebank_common_transact.c261
1 files changed, 261 insertions, 0 deletions
diff --git a/src/bank-lib/fakebank_common_transact.c b/src/bank-lib/fakebank_common_transact.c
new file mode 100644
index 000000000..a099ef966
--- /dev/null
+++ b/src/bank-lib/fakebank_common_transact.c
@@ -0,0 +1,261 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_transact.c
+ * @brief actual transaction logic for FAKEBANK
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_common_transact.h"
+
+
+/**
+ * Update @a account balance by @a amount.
+ *
+ * The @a big_lock 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,out] h bank handle
+ * @param[in,out] t transaction to add to account lists
+ */
+void
+TALER_FAKEBANK_transact_ (struct TALER_FAKEBANK_Handle *h,
+ struct Transaction *t)
+{
+ struct Account *debit_acc = t->debit_account;
+ struct Account *credit_acc = t->credit_account;
+ uint64_t row_id;
+ struct Transaction *old;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ row_id = ++h->serial_counter;
+ old = h->transactions[row_id % h->ram_limit];
+ h->transactions[row_id % h->ram_limit] = t;
+ t->row_id = row_id;
+ GNUNET_CONTAINER_MDLL_insert_tail (out,
+ debit_acc->out_head,
+ debit_acc->out_tail,
+ t);
+ update_balance (debit_acc,
+ &t->amount,
+ true);
+ GNUNET_CONTAINER_MDLL_insert_tail (in,
+ credit_acc->in_head,
+ credit_acc->in_tail,
+ t);
+ update_balance (credit_acc,
+ &t->amount,
+ false);
+ if (NULL != old)
+ {
+ struct Account *da;
+ struct Account *ca;
+
+ da = old->debit_account;
+ ca = old->credit_account;
+ /* slot was already in use, must clean out old
+ entry first! */
+ GNUNET_CONTAINER_MDLL_remove (out,
+ da->out_head,
+ da->out_tail,
+ old);
+ GNUNET_CONTAINER_MDLL_remove (in,
+ ca->in_head,
+ ca->in_tail,
+ old);
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if ( (NULL != old) &&
+ (T_DEBIT == old->type) )
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->uuid_map_lock));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_remove (h->uuid_map,
+ &old->request_uid,
+ old));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ }
+ GNUNET_free (old);
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_make_transfer_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const char *debit_account,
+ const char *credit_account,
+ const struct TALER_Amount *amount,
+ const struct TALER_WireTransferIdentifierRawP *subject,
+ const char *exchange_base_url,
+ const struct GNUNET_HashCode *request_uid,
+ uint64_t *ret_row_id,
+ struct GNUNET_TIME_Timestamp *timestamp)
+{
+ struct Transaction *t;
+ struct Account *debit_acc;
+ struct Account *credit_acc;
+ size_t url_len;
+
+ GNUNET_assert (0 == strcasecmp (amount->currency,
+ h->currency));
+ GNUNET_assert (NULL != debit_account);
+ GNUNET_assert (NULL != credit_account);
+ GNUNET_break (0 != strncasecmp ("payto://",
+ debit_account,
+ strlen ("payto://")));
+ GNUNET_break (0 != strncasecmp ("payto://",
+ credit_account,
+ strlen ("payto://")));
+ url_len = strlen (exchange_base_url);
+ GNUNET_assert (url_len < MAX_URL_LEN);
+ debit_acc = TALER_FAKEBANK_lookup_account_ (h,
+ debit_account,
+ debit_account);
+ credit_acc = TALER_FAKEBANK_lookup_account_ (h,
+ credit_account,
+ credit_account);
+ if (NULL != request_uid)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->uuid_map_lock));
+ t = GNUNET_CONTAINER_multihashmap_get (h->uuid_map,
+ request_uid);
+ if (NULL != t)
+ {
+ if ( (debit_acc != t->debit_account) ||
+ (credit_acc != t->credit_account) ||
+ (0 != TALER_amount_cmp (amount,
+ &t->amount)) ||
+ (T_DEBIT != t->type) ||
+ (0 != GNUNET_memcmp (subject,
+ &t->subject.debit.wtid)) )
+ {
+ /* Transaction exists, but with different details. */
+ GNUNET_break (0);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ return GNUNET_SYSERR;
+ }
+ *ret_row_id = t->row_id;
+ *timestamp = t->date;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ return GNUNET_OK;
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ }
+ t = GNUNET_new (struct Transaction);
+ t->unchecked = true;
+ t->debit_account = debit_acc;
+ t->credit_account = credit_acc;
+ t->amount = *amount;
+ t->date = GNUNET_TIME_timestamp_get ();
+ if (NULL != timestamp)
+ *timestamp = t->date;
+ t->type = T_DEBIT;
+ GNUNET_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;
+ TALER_FAKEBANK_transact_ (h,
+ 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_log (GNUNET_ERROR_TYPE_INFO,
+ "Making transfer %llu from %s to %s over %s and subject %s; for exchange: %s\n",
+ (unsigned long long) t->row_id,
+ debit_account,
+ credit_account,
+ TALER_amount2s (amount),
+ TALER_B2S (subject),
+ exchange_base_url);
+ *ret_row_id = t->row_id;
+ TALER_FAKEBANK_notify_transaction_ (h,
+ t);
+ return GNUNET_OK;
+}