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 (8841B)


      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 <pthread.h>
     25 #include "taler/taler_fakebank_lib.h"
     26 #include "taler/taler_bank_service.h"
     27 #include "taler/taler_mhd_lib.h"
     28 #include <gnunet/gnunet_mhd_compat.h>
     29 #include "fakebank.h"
     30 #include "fakebank_common_lookup.h"
     31 #include "fakebank_common_lp.h"
     32 #include "fakebank_common_transact.h"
     33 
     34 
     35 /**
     36  * Update @a account balance by @a amount.
     37  *
     38  * The @a big_lock must already be locked when calling
     39  * this function.
     40  *
     41  * @param[in,out] account account to update
     42  * @param amount balance change
     43  * @param debit true to subtract, false to add @a amount
     44  */
     45 static void
     46 update_balance (struct Account *account,
     47                 const struct TALER_Amount *amount,
     48                 bool debit)
     49 {
     50   if (debit == account->is_negative)
     51   {
     52     GNUNET_assert (0 <=
     53                    TALER_amount_add (&account->balance,
     54                                      &account->balance,
     55                                      amount));
     56     return;
     57   }
     58   if (0 <= TALER_amount_cmp (&account->balance,
     59                              amount))
     60   {
     61     GNUNET_assert (0 <=
     62                    TALER_amount_subtract (&account->balance,
     63                                           &account->balance,
     64                                           amount));
     65   }
     66   else
     67   {
     68     GNUNET_assert (0 <=
     69                    TALER_amount_subtract (&account->balance,
     70                                           amount,
     71                                           &account->balance));
     72     account->is_negative = ! account->is_negative;
     73   }
     74 }
     75 
     76 
     77 /**
     78  * Add transaction to the debit and credit accounts,
     79  * updating the balances as needed.
     80  *
     81  * The transaction @a t must already be locked
     82  * when calling this function!
     83  *
     84  * @param[in,out] h bank handle
     85  * @param[in,out] t transaction to add to account lists
     86  */
     87 void
     88 TALER_FAKEBANK_transact_ (struct TALER_FAKEBANK_Handle *h,
     89                           struct Transaction *t)
     90 {
     91   struct Account *debit_acc = t->debit_account;
     92   struct Account *credit_acc = t->credit_account;
     93   uint64_t row_id;
     94   struct Transaction *old;
     95 
     96   GNUNET_assert (0 ==
     97                  pthread_mutex_lock (&h->big_lock));
     98   row_id = ++h->serial_counter;
     99   old = h->transactions[row_id % h->ram_limit];
    100   h->transactions[row_id % h->ram_limit] = t;
    101   t->row_id = row_id;
    102   GNUNET_CONTAINER_MDLL_insert_tail (out,
    103                                      debit_acc->out_head,
    104                                      debit_acc->out_tail,
    105                                      t);
    106   update_balance (debit_acc,
    107                   &t->amount,
    108                   true);
    109   GNUNET_CONTAINER_MDLL_insert_tail (in,
    110                                      credit_acc->in_head,
    111                                      credit_acc->in_tail,
    112                                      t);
    113   update_balance (credit_acc,
    114                   &t->amount,
    115                   false);
    116   if (NULL != old)
    117   {
    118     struct Account *da;
    119     struct Account *ca;
    120 
    121     da = old->debit_account;
    122     ca = old->credit_account;
    123     /* slot was already in use, must clean out old
    124        entry first! */
    125     GNUNET_CONTAINER_MDLL_remove (out,
    126                                   da->out_head,
    127                                   da->out_tail,
    128                                   old);
    129     GNUNET_CONTAINER_MDLL_remove (in,
    130                                   ca->in_head,
    131                                   ca->in_tail,
    132                                   old);
    133   }
    134   GNUNET_assert (0 ==
    135                  pthread_mutex_unlock (&h->big_lock));
    136   if ( (NULL != old) &&
    137        (T_DEBIT == old->type) )
    138   {
    139     GNUNET_assert (0 ==
    140                    pthread_mutex_lock (&h->uuid_map_lock));
    141     GNUNET_assert (GNUNET_OK ==
    142                    GNUNET_CONTAINER_multihashmap_remove (h->uuid_map,
    143                                                          &old->request_uid,
    144                                                          old));
    145     GNUNET_assert (0 ==
    146                    pthread_mutex_unlock (&h->uuid_map_lock));
    147   }
    148   GNUNET_free (old);
    149 }
    150 
    151 
    152 enum GNUNET_GenericReturnValue
    153 TALER_FAKEBANK_make_transfer_ (
    154   struct TALER_FAKEBANK_Handle *h,
    155   const char *debit_account,
    156   const char *credit_account,
    157   const struct TALER_Amount *amount,
    158   const struct TALER_WireTransferIdentifierRawP *subject,
    159   const char *exchange_base_url,
    160   const struct GNUNET_HashCode *request_uid,
    161   uint64_t *ret_row_id,
    162   struct GNUNET_TIME_Timestamp *timestamp)
    163 {
    164   struct Transaction *t;
    165   struct Account *debit_acc;
    166   struct Account *credit_acc;
    167   size_t url_len;
    168 
    169   GNUNET_assert (0 == strcasecmp (amount->currency,
    170                                   h->currency));
    171   GNUNET_assert (NULL != debit_account);
    172   GNUNET_assert (NULL != credit_account);
    173   GNUNET_break (0 != strncasecmp ("payto://",
    174                                   debit_account,
    175                                   strlen ("payto://")));
    176   GNUNET_break (0 != strncasecmp ("payto://",
    177                                   credit_account,
    178                                   strlen ("payto://")));
    179   url_len = strlen (exchange_base_url);
    180   GNUNET_assert (url_len < MAX_URL_LEN);
    181   debit_acc = TALER_FAKEBANK_lookup_account_ (h,
    182                                               debit_account,
    183                                               debit_account);
    184   credit_acc = TALER_FAKEBANK_lookup_account_ (h,
    185                                                credit_account,
    186                                                credit_account);
    187   if (NULL != request_uid)
    188   {
    189     GNUNET_assert (0 ==
    190                    pthread_mutex_lock (&h->uuid_map_lock));
    191     t = GNUNET_CONTAINER_multihashmap_get (h->uuid_map,
    192                                            request_uid);
    193     if (NULL != t)
    194     {
    195       if ( (debit_acc != t->debit_account) ||
    196            (credit_acc != t->credit_account) ||
    197            (0 != TALER_amount_cmp (amount,
    198                                    &t->amount)) ||
    199            (T_DEBIT != t->type) ||
    200            (0 != GNUNET_memcmp (subject,
    201                                 &t->subject.debit.wtid)) )
    202       {
    203         /* Transaction exists, but with different details. */
    204         GNUNET_break (0);
    205         GNUNET_assert (0 ==
    206                        pthread_mutex_unlock (&h->uuid_map_lock));
    207         return GNUNET_SYSERR;
    208       }
    209       *ret_row_id = t->row_id;
    210       *timestamp = t->date;
    211       GNUNET_assert (0 ==
    212                      pthread_mutex_unlock (&h->uuid_map_lock));
    213       return GNUNET_OK;
    214     }
    215     GNUNET_assert (0 ==
    216                    pthread_mutex_unlock (&h->uuid_map_lock));
    217   }
    218   t = GNUNET_new (struct Transaction);
    219   t->unchecked = true;
    220   t->debit_account = debit_acc;
    221   t->credit_account = credit_acc;
    222   t->amount = *amount;
    223   t->date = GNUNET_TIME_timestamp_get ();
    224   if (NULL != timestamp)
    225     *timestamp = t->date;
    226   t->type = T_DEBIT;
    227   GNUNET_memcpy (t->subject.debit.exchange_base_url,
    228                  exchange_base_url,
    229                  url_len);
    230   t->subject.debit.wtid = *subject;
    231   if (NULL == request_uid)
    232     GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
    233                                       &t->request_uid);
    234   else
    235     t->request_uid = *request_uid;
    236   TALER_FAKEBANK_transact_ (h,
    237                             t);
    238   GNUNET_assert (0 ==
    239                  pthread_mutex_lock (&h->uuid_map_lock));
    240   GNUNET_assert (GNUNET_OK ==
    241                  GNUNET_CONTAINER_multihashmap_put (
    242                    h->uuid_map,
    243                    &t->request_uid,
    244                    t,
    245                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
    246   GNUNET_assert (0 ==
    247                  pthread_mutex_unlock (&h->uuid_map_lock));
    248   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    249               "Making transfer %llu from %s to %s over %s and subject %s; for exchange: %s\n",
    250               (unsigned long long) t->row_id,
    251               debit_account,
    252               credit_account,
    253               TALER_amount2s (amount),
    254               TALER_B2S (subject),
    255               exchange_base_url);
    256   *ret_row_id = t->row_id;
    257   TALER_FAKEBANK_notify_transaction_ (h,
    258                                       t);
    259   return GNUNET_OK;
    260 }