/*
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
*/
/**
* @file bank-lib/fakebank_common_transact.c
* @brief actual transaction logic for FAKEBANK
* @author Christian Grothoff
*/
#include "platform.h"
#include
#include "taler_fakebank_lib.h"
#include "taler_bank_service.h"
#include "taler_mhd_lib.h"
#include
#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;
}