exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

fakebank_common_transact.c (8869B)


      1 /*
      2   This file is part of TALER
      3   (C) 2016-2023 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or
      6   modify it under the terms of the GNU General Public License
      7   as published by the Free Software Foundation; either version 3,
      8   or (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful,
     11   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with TALER; see the file COPYING.  If not,
     17   see <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file bank-lib/fakebank_common_transact.c
     21  * @brief actual transaction logic for FAKEBANK
     22  * @author Christian Grothoff <christian@grothoff.org>
     23  */
     24 #include "taler/platform.h"
     25 #include <pthread.h>
     26 #include "taler/taler_fakebank_lib.h"
     27 #include "taler/taler_bank_service.h"
     28 #include "taler/taler_mhd_lib.h"
     29 #include <gnunet/gnunet_mhd_compat.h>
     30 #include "fakebank.h"
     31 #include "fakebank_common_lookup.h"
     32 #include "fakebank_common_lp.h"
     33 #include "fakebank_common_transact.h"
     34 
     35 
     36 /**
     37  * Update @a account balance by @a amount.
     38  *
     39  * The @a big_lock must already be locked when calling
     40  * this function.
     41  *
     42  * @param[in,out] account account to update
     43  * @param amount balance change
     44  * @param debit true to subtract, false to add @a amount
     45  */
     46 static void
     47 update_balance (struct Account *account,
     48                 const struct TALER_Amount *amount,
     49                 bool debit)
     50 {
     51   if (debit == account->is_negative)
     52   {
     53     GNUNET_assert (0 <=
     54                    TALER_amount_add (&account->balance,
     55                                      &account->balance,
     56                                      amount));
     57     return;
     58   }
     59   if (0 <= TALER_amount_cmp (&account->balance,
     60                              amount))
     61   {
     62     GNUNET_assert (0 <=
     63                    TALER_amount_subtract (&account->balance,
     64                                           &account->balance,
     65                                           amount));
     66   }
     67   else
     68   {
     69     GNUNET_assert (0 <=
     70                    TALER_amount_subtract (&account->balance,
     71                                           amount,
     72                                           &account->balance));
     73     account->is_negative = ! account->is_negative;
     74   }
     75 }
     76 
     77 
     78 /**
     79  * Add transaction to the debit and credit accounts,
     80  * updating the balances as needed.
     81  *
     82  * The transaction @a t must already be locked
     83  * when calling this function!
     84  *
     85  * @param[in,out] h bank handle
     86  * @param[in,out] t transaction to add to account lists
     87  */
     88 void
     89 TALER_FAKEBANK_transact_ (struct TALER_FAKEBANK_Handle *h,
     90                           struct Transaction *t)
     91 {
     92   struct Account *debit_acc = t->debit_account;
     93   struct Account *credit_acc = t->credit_account;
     94   uint64_t row_id;
     95   struct Transaction *old;
     96 
     97   GNUNET_assert (0 ==
     98                  pthread_mutex_lock (&h->big_lock));
     99   row_id = ++h->serial_counter;
    100   old = h->transactions[row_id % h->ram_limit];
    101   h->transactions[row_id % h->ram_limit] = t;
    102   t->row_id = row_id;
    103   GNUNET_CONTAINER_MDLL_insert_tail (out,
    104                                      debit_acc->out_head,
    105                                      debit_acc->out_tail,
    106                                      t);
    107   update_balance (debit_acc,
    108                   &t->amount,
    109                   true);
    110   GNUNET_CONTAINER_MDLL_insert_tail (in,
    111                                      credit_acc->in_head,
    112                                      credit_acc->in_tail,
    113                                      t);
    114   update_balance (credit_acc,
    115                   &t->amount,
    116                   false);
    117   if (NULL != old)
    118   {
    119     struct Account *da;
    120     struct Account *ca;
    121 
    122     da = old->debit_account;
    123     ca = old->credit_account;
    124     /* slot was already in use, must clean out old
    125        entry first! */
    126     GNUNET_CONTAINER_MDLL_remove (out,
    127                                   da->out_head,
    128                                   da->out_tail,
    129                                   old);
    130     GNUNET_CONTAINER_MDLL_remove (in,
    131                                   ca->in_head,
    132                                   ca->in_tail,
    133                                   old);
    134   }
    135   GNUNET_assert (0 ==
    136                  pthread_mutex_unlock (&h->big_lock));
    137   if ( (NULL != old) &&
    138        (T_DEBIT == old->type) )
    139   {
    140     GNUNET_assert (0 ==
    141                    pthread_mutex_lock (&h->uuid_map_lock));
    142     GNUNET_assert (GNUNET_OK ==
    143                    GNUNET_CONTAINER_multihashmap_remove (h->uuid_map,
    144                                                          &old->request_uid,
    145                                                          old));
    146     GNUNET_assert (0 ==
    147                    pthread_mutex_unlock (&h->uuid_map_lock));
    148   }
    149   GNUNET_free (old);
    150 }
    151 
    152 
    153 enum GNUNET_GenericReturnValue
    154 TALER_FAKEBANK_make_transfer_ (
    155   struct TALER_FAKEBANK_Handle *h,
    156   const char *debit_account,
    157   const char *credit_account,
    158   const struct TALER_Amount *amount,
    159   const struct TALER_WireTransferIdentifierRawP *subject,
    160   const char *exchange_base_url,
    161   const struct GNUNET_HashCode *request_uid,
    162   uint64_t *ret_row_id,
    163   struct GNUNET_TIME_Timestamp *timestamp)
    164 {
    165   struct Transaction *t;
    166   struct Account *debit_acc;
    167   struct Account *credit_acc;
    168   size_t url_len;
    169 
    170   GNUNET_assert (0 == strcasecmp (amount->currency,
    171                                   h->currency));
    172   GNUNET_assert (NULL != debit_account);
    173   GNUNET_assert (NULL != credit_account);
    174   GNUNET_break (0 != strncasecmp ("payto://",
    175                                   debit_account,
    176                                   strlen ("payto://")));
    177   GNUNET_break (0 != strncasecmp ("payto://",
    178                                   credit_account,
    179                                   strlen ("payto://")));
    180   url_len = strlen (exchange_base_url);
    181   GNUNET_assert (url_len < MAX_URL_LEN);
    182   debit_acc = TALER_FAKEBANK_lookup_account_ (h,
    183                                               debit_account,
    184                                               debit_account);
    185   credit_acc = TALER_FAKEBANK_lookup_account_ (h,
    186                                                credit_account,
    187                                                credit_account);
    188   if (NULL != request_uid)
    189   {
    190     GNUNET_assert (0 ==
    191                    pthread_mutex_lock (&h->uuid_map_lock));
    192     t = GNUNET_CONTAINER_multihashmap_get (h->uuid_map,
    193                                            request_uid);
    194     if (NULL != t)
    195     {
    196       if ( (debit_acc != t->debit_account) ||
    197            (credit_acc != t->credit_account) ||
    198            (0 != TALER_amount_cmp (amount,
    199                                    &t->amount)) ||
    200            (T_DEBIT != t->type) ||
    201            (0 != GNUNET_memcmp (subject,
    202                                 &t->subject.debit.wtid)) )
    203       {
    204         /* Transaction exists, but with different details. */
    205         GNUNET_break (0);
    206         GNUNET_assert (0 ==
    207                        pthread_mutex_unlock (&h->uuid_map_lock));
    208         return GNUNET_SYSERR;
    209       }
    210       *ret_row_id = t->row_id;
    211       *timestamp = t->date;
    212       GNUNET_assert (0 ==
    213                      pthread_mutex_unlock (&h->uuid_map_lock));
    214       return GNUNET_OK;
    215     }
    216     GNUNET_assert (0 ==
    217                    pthread_mutex_unlock (&h->uuid_map_lock));
    218   }
    219   t = GNUNET_new (struct Transaction);
    220   t->unchecked = true;
    221   t->debit_account = debit_acc;
    222   t->credit_account = credit_acc;
    223   t->amount = *amount;
    224   t->date = GNUNET_TIME_timestamp_get ();
    225   if (NULL != timestamp)
    226     *timestamp = t->date;
    227   t->type = T_DEBIT;
    228   GNUNET_memcpy (t->subject.debit.exchange_base_url,
    229                  exchange_base_url,
    230                  url_len);
    231   t->subject.debit.wtid = *subject;
    232   if (NULL == request_uid)
    233     GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
    234                                       &t->request_uid);
    235   else
    236     t->request_uid = *request_uid;
    237   TALER_FAKEBANK_transact_ (h,
    238                             t);
    239   GNUNET_assert (0 ==
    240                  pthread_mutex_lock (&h->uuid_map_lock));
    241   GNUNET_assert (GNUNET_OK ==
    242                  GNUNET_CONTAINER_multihashmap_put (
    243                    h->uuid_map,
    244                    &t->request_uid,
    245                    t,
    246                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
    247   GNUNET_assert (0 ==
    248                  pthread_mutex_unlock (&h->uuid_map_lock));
    249   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    250               "Making transfer %llu from %s to %s over %s and subject %s; for exchange: %s\n",
    251               (unsigned long long) t->row_id,
    252               debit_account,
    253               credit_account,
    254               TALER_amount2s (amount),
    255               TALER_B2S (subject),
    256               exchange_base_url);
    257   *ret_row_id = t->row_id;
    258   TALER_FAKEBANK_notify_transaction_ (h,
    259                                       t);
    260   return GNUNET_OK;
    261 }