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 }