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


      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     return;
    138   switch (old->type)
    139   {
    140   case T_DEBIT:
    141     GNUNET_assert (0 ==
    142                    pthread_mutex_lock (&h->uuid_map_lock));
    143     GNUNET_assert (GNUNET_OK ==
    144                    GNUNET_CONTAINER_multihashmap_remove (h->uuid_map,
    145                                                          &old->request_uid,
    146                                                          old));
    147     GNUNET_assert (0 ==
    148                    pthread_mutex_unlock (&h->uuid_map_lock));
    149     break;
    150   case T_CREDIT:
    151     {
    152       const struct GNUNET_PeerIdentity *pid
    153         = (const struct GNUNET_PeerIdentity *) &old->subject.credit.reserve_pub;
    154 
    155       /* admin transfers are indexed in rpubs by reserve_pub; drop the
    156          secondary index before freeing to avoid a dangling pointer */
    157       GNUNET_assert (0 ==
    158                      pthread_mutex_lock (&h->rpubs_lock));
    159       GNUNET_assert (GNUNET_OK ==
    160                      GNUNET_CONTAINER_multipeermap_remove (h->rpubs,
    161                                                            pid,
    162                                                            old));
    163       GNUNET_assert (0 ==
    164                      pthread_mutex_unlock (&h->rpubs_lock));
    165     }
    166     break;
    167   case T_AUTH:
    168     /* FIXME: anything to clean up? */
    169     break;
    170   case T_WAD:
    171     break;
    172   }
    173   GNUNET_free (old);
    174 }
    175 
    176 
    177 enum GNUNET_GenericReturnValue
    178 TALER_FAKEBANK_make_transfer_ (
    179   struct TALER_FAKEBANK_Handle *h,
    180   const char *debit_account,
    181   const char *credit_account,
    182   const struct TALER_Amount *amount,
    183   const struct TALER_WireTransferIdentifierRawP *subject,
    184   const char *exchange_base_url,
    185   const struct GNUNET_HashCode *request_uid,
    186   uint64_t *ret_row_id,
    187   struct GNUNET_TIME_Timestamp *timestamp)
    188 {
    189   struct Transaction *t;
    190   struct Account *debit_acc;
    191   struct Account *credit_acc;
    192   size_t url_len;
    193 
    194   GNUNET_assert (0 == strcasecmp (amount->currency,
    195                                   h->currency));
    196   GNUNET_assert (NULL != debit_account);
    197   GNUNET_assert (NULL != credit_account);
    198   GNUNET_break (0 != strncasecmp ("payto://",
    199                                   debit_account,
    200                                   strlen ("payto://")));
    201   GNUNET_break (0 != strncasecmp ("payto://",
    202                                   credit_account,
    203                                   strlen ("payto://")));
    204   url_len = strlen (exchange_base_url);
    205   GNUNET_assert (url_len < MAX_URL_LEN);
    206   debit_acc = TALER_FAKEBANK_lookup_account_ (h,
    207                                               debit_account,
    208                                               debit_account);
    209   credit_acc = TALER_FAKEBANK_lookup_account_ (h,
    210                                                credit_account,
    211                                                credit_account);
    212   if (NULL != request_uid)
    213   {
    214     GNUNET_assert (0 ==
    215                    pthread_mutex_lock (&h->uuid_map_lock));
    216     t = GNUNET_CONTAINER_multihashmap_get (h->uuid_map,
    217                                            request_uid);
    218     if (NULL != t)
    219     {
    220       if ( (debit_acc != t->debit_account) ||
    221            (credit_acc != t->credit_account) ||
    222            (0 != TALER_amount_cmp (amount,
    223                                    &t->amount)) ||
    224            (T_DEBIT != t->type) ||
    225            (0 != GNUNET_memcmp (subject,
    226                                 &t->subject.debit.wtid)) )
    227       {
    228         /* Transaction exists, but with different details. */
    229         GNUNET_break (0);
    230         GNUNET_assert (0 ==
    231                        pthread_mutex_unlock (&h->uuid_map_lock));
    232         return GNUNET_SYSERR;
    233       }
    234       *ret_row_id = t->row_id;
    235       *timestamp = t->date;
    236       GNUNET_assert (0 ==
    237                      pthread_mutex_unlock (&h->uuid_map_lock));
    238       return GNUNET_OK;
    239     }
    240     GNUNET_assert (0 ==
    241                    pthread_mutex_unlock (&h->uuid_map_lock));
    242   }
    243   t = GNUNET_new (struct Transaction);
    244   t->unchecked = true;
    245   t->debit_account = debit_acc;
    246   t->credit_account = credit_acc;
    247   t->amount = *amount;
    248   t->date = GNUNET_TIME_timestamp_get ();
    249   if (NULL != timestamp)
    250     *timestamp = t->date;
    251   t->type = T_DEBIT;
    252   GNUNET_memcpy (t->subject.debit.exchange_base_url,
    253                  exchange_base_url,
    254                  url_len);
    255   t->subject.debit.wtid = *subject;
    256   if (NULL == request_uid)
    257     GNUNET_CRYPTO_random_block (&t->request_uid,
    258                                 sizeof t->request_uid);
    259   else
    260     t->request_uid = *request_uid;
    261   TALER_FAKEBANK_transact_ (h,
    262                             t);
    263   GNUNET_assert (0 ==
    264                  pthread_mutex_lock (&h->uuid_map_lock));
    265   GNUNET_assert (GNUNET_OK ==
    266                  GNUNET_CONTAINER_multihashmap_put (
    267                    h->uuid_map,
    268                    &t->request_uid,
    269                    t,
    270                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
    271   GNUNET_assert (0 ==
    272                  pthread_mutex_unlock (&h->uuid_map_lock));
    273   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    274               "Making transfer %llu from %s to %s over %s and subject %s; for exchange: %s\n",
    275               (unsigned long long) t->row_id,
    276               debit_account,
    277               credit_account,
    278               TALER_amount2s (amount),
    279               TALER_B2S (subject),
    280               exchange_base_url);
    281   *ret_row_id = t->row_id;
    282   TALER_FAKEBANK_notify_transaction_ (h,
    283                                       t);
    284   return GNUNET_OK;
    285 }