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 }