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 }