Line data Source code
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 254 : update_balance (struct Account *account,
48 : const struct TALER_Amount *amount,
49 : bool debit)
50 : {
51 254 : if (debit == account->is_negative)
52 : {
53 175 : GNUNET_assert (0 <=
54 : TALER_amount_add (&account->balance,
55 : &account->balance,
56 : amount));
57 175 : return;
58 : }
59 79 : if (0 <= TALER_amount_cmp (&account->balance,
60 : amount))
61 : {
62 56 : GNUNET_assert (0 <=
63 : TALER_amount_subtract (&account->balance,
64 : &account->balance,
65 : amount));
66 : }
67 : else
68 : {
69 23 : GNUNET_assert (0 <=
70 : TALER_amount_subtract (&account->balance,
71 : amount,
72 : &account->balance));
73 23 : 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 127 : TALER_FAKEBANK_transact_ (struct TALER_FAKEBANK_Handle *h,
90 : struct Transaction *t)
91 : {
92 127 : struct Account *debit_acc = t->debit_account;
93 127 : struct Account *credit_acc = t->credit_account;
94 : uint64_t row_id;
95 : struct Transaction *old;
96 :
97 127 : GNUNET_assert (0 ==
98 : pthread_mutex_lock (&h->big_lock));
99 127 : row_id = ++h->serial_counter;
100 127 : old = h->transactions[row_id % h->ram_limit];
101 127 : h->transactions[row_id % h->ram_limit] = t;
102 127 : t->row_id = row_id;
103 127 : GNUNET_CONTAINER_MDLL_insert_tail (out,
104 : debit_acc->out_head,
105 : debit_acc->out_tail,
106 : t);
107 127 : update_balance (debit_acc,
108 127 : &t->amount,
109 : true);
110 127 : GNUNET_CONTAINER_MDLL_insert_tail (in,
111 : credit_acc->in_head,
112 : credit_acc->in_tail,
113 : t);
114 127 : update_balance (credit_acc,
115 127 : &t->amount,
116 : false);
117 127 : if (NULL != old)
118 : {
119 : struct Account *da;
120 : struct Account *ca;
121 :
122 0 : da = old->debit_account;
123 0 : ca = old->credit_account;
124 : /* slot was already in use, must clean out old
125 : entry first! */
126 0 : GNUNET_CONTAINER_MDLL_remove (out,
127 : da->out_head,
128 : da->out_tail,
129 : old);
130 0 : GNUNET_CONTAINER_MDLL_remove (in,
131 : ca->in_head,
132 : ca->in_tail,
133 : old);
134 : }
135 127 : GNUNET_assert (0 ==
136 : pthread_mutex_unlock (&h->big_lock));
137 127 : if ( (NULL != old) &&
138 0 : (T_DEBIT == old->type) )
139 : {
140 0 : GNUNET_assert (0 ==
141 : pthread_mutex_lock (&h->uuid_map_lock));
142 0 : GNUNET_assert (GNUNET_OK ==
143 : GNUNET_CONTAINER_multihashmap_remove (h->uuid_map,
144 : &old->request_uid,
145 : old));
146 0 : GNUNET_assert (0 ==
147 : pthread_mutex_unlock (&h->uuid_map_lock));
148 : }
149 127 : GNUNET_free (old);
150 127 : }
151 :
152 :
153 : enum GNUNET_GenericReturnValue
154 54 : 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 54 : GNUNET_assert (0 == strcasecmp (amount->currency,
171 : h->currency));
172 54 : GNUNET_assert (NULL != debit_account);
173 54 : GNUNET_assert (NULL != credit_account);
174 54 : GNUNET_break (0 != strncasecmp ("payto://",
175 : debit_account,
176 : strlen ("payto://")));
177 54 : GNUNET_break (0 != strncasecmp ("payto://",
178 : credit_account,
179 : strlen ("payto://")));
180 54 : url_len = strlen (exchange_base_url);
181 54 : GNUNET_assert (url_len < MAX_URL_LEN);
182 54 : debit_acc = TALER_FAKEBANK_lookup_account_ (h,
183 : debit_account,
184 : debit_account);
185 54 : credit_acc = TALER_FAKEBANK_lookup_account_ (h,
186 : credit_account,
187 : credit_account);
188 54 : if (NULL != request_uid)
189 : {
190 54 : GNUNET_assert (0 ==
191 : pthread_mutex_lock (&h->uuid_map_lock));
192 54 : t = GNUNET_CONTAINER_multihashmap_get (h->uuid_map,
193 : request_uid);
194 54 : if (NULL != t)
195 : {
196 0 : if ( (debit_acc != t->debit_account) ||
197 0 : (credit_acc != t->credit_account) ||
198 0 : (0 != TALER_amount_cmp (amount,
199 0 : &t->amount)) ||
200 0 : (T_DEBIT != t->type) ||
201 0 : (0 != GNUNET_memcmp (subject,
202 : &t->subject.debit.wtid)) )
203 : {
204 : /* Transaction exists, but with different details. */
205 0 : GNUNET_break (0);
206 0 : GNUNET_assert (0 ==
207 : pthread_mutex_unlock (&h->uuid_map_lock));
208 0 : return GNUNET_SYSERR;
209 : }
210 0 : *ret_row_id = t->row_id;
211 0 : *timestamp = t->date;
212 0 : GNUNET_assert (0 ==
213 : pthread_mutex_unlock (&h->uuid_map_lock));
214 0 : return GNUNET_OK;
215 : }
216 54 : GNUNET_assert (0 ==
217 : pthread_mutex_unlock (&h->uuid_map_lock));
218 : }
219 54 : t = GNUNET_new (struct Transaction);
220 54 : t->unchecked = true;
221 54 : t->debit_account = debit_acc;
222 54 : t->credit_account = credit_acc;
223 54 : t->amount = *amount;
224 54 : t->date = GNUNET_TIME_timestamp_get ();
225 54 : if (NULL != timestamp)
226 54 : *timestamp = t->date;
227 54 : t->type = T_DEBIT;
228 54 : GNUNET_memcpy (t->subject.debit.exchange_base_url,
229 : exchange_base_url,
230 : url_len);
231 54 : t->subject.debit.wtid = *subject;
232 54 : if (NULL == request_uid)
233 0 : GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
234 : &t->request_uid);
235 : else
236 54 : t->request_uid = *request_uid;
237 54 : TALER_FAKEBANK_transact_ (h,
238 : t);
239 54 : GNUNET_assert (0 ==
240 : pthread_mutex_lock (&h->uuid_map_lock));
241 54 : 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 54 : GNUNET_assert (0 ==
248 : pthread_mutex_unlock (&h->uuid_map_lock));
249 54 : 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 54 : *ret_row_id = t->row_id;
258 54 : TALER_FAKEBANK_notify_transaction_ (h,
259 : t);
260 54 : return GNUNET_OK;
261 : }
|