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 <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 260 : update_balance (struct Account *account,
47 : const struct TALER_Amount *amount,
48 : bool debit)
49 : {
50 260 : if (debit == account->is_negative)
51 : {
52 178 : GNUNET_assert (0 <=
53 : TALER_amount_add (&account->balance,
54 : &account->balance,
55 : amount));
56 178 : return;
57 : }
58 82 : if (0 <= TALER_amount_cmp (&account->balance,
59 : amount))
60 : {
61 56 : GNUNET_assert (0 <=
62 : TALER_amount_subtract (&account->balance,
63 : &account->balance,
64 : amount));
65 : }
66 : else
67 : {
68 26 : GNUNET_assert (0 <=
69 : TALER_amount_subtract (&account->balance,
70 : amount,
71 : &account->balance));
72 26 : 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 130 : TALER_FAKEBANK_transact_ (struct TALER_FAKEBANK_Handle *h,
89 : struct Transaction *t)
90 : {
91 130 : struct Account *debit_acc = t->debit_account;
92 130 : struct Account *credit_acc = t->credit_account;
93 : uint64_t row_id;
94 : struct Transaction *old;
95 :
96 130 : GNUNET_assert (0 ==
97 : pthread_mutex_lock (&h->big_lock));
98 130 : row_id = ++h->serial_counter;
99 130 : old = h->transactions[row_id % h->ram_limit];
100 130 : h->transactions[row_id % h->ram_limit] = t;
101 130 : t->row_id = row_id;
102 130 : GNUNET_CONTAINER_MDLL_insert_tail (out,
103 : debit_acc->out_head,
104 : debit_acc->out_tail,
105 : t);
106 130 : update_balance (debit_acc,
107 130 : &t->amount,
108 : true);
109 130 : GNUNET_CONTAINER_MDLL_insert_tail (in,
110 : credit_acc->in_head,
111 : credit_acc->in_tail,
112 : t);
113 130 : update_balance (credit_acc,
114 130 : &t->amount,
115 : false);
116 130 : if (NULL != old)
117 : {
118 : struct Account *da;
119 : struct Account *ca;
120 :
121 0 : da = old->debit_account;
122 0 : ca = old->credit_account;
123 : /* slot was already in use, must clean out old
124 : entry first! */
125 0 : GNUNET_CONTAINER_MDLL_remove (out,
126 : da->out_head,
127 : da->out_tail,
128 : old);
129 0 : GNUNET_CONTAINER_MDLL_remove (in,
130 : ca->in_head,
131 : ca->in_tail,
132 : old);
133 : }
134 130 : GNUNET_assert (0 ==
135 : pthread_mutex_unlock (&h->big_lock));
136 130 : if (NULL == old)
137 130 : return;
138 0 : switch (old->type)
139 : {
140 0 : case T_DEBIT:
141 0 : GNUNET_assert (0 ==
142 : pthread_mutex_lock (&h->uuid_map_lock));
143 0 : GNUNET_assert (GNUNET_OK ==
144 : GNUNET_CONTAINER_multihashmap_remove (h->uuid_map,
145 : &old->request_uid,
146 : old));
147 0 : GNUNET_assert (0 ==
148 : pthread_mutex_unlock (&h->uuid_map_lock));
149 0 : break;
150 0 : case T_CREDIT:
151 : {
152 0 : 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 0 : GNUNET_assert (0 ==
158 : pthread_mutex_lock (&h->rpubs_lock));
159 0 : GNUNET_assert (GNUNET_OK ==
160 : GNUNET_CONTAINER_multipeermap_remove (h->rpubs,
161 : pid,
162 : old));
163 0 : GNUNET_assert (0 ==
164 : pthread_mutex_unlock (&h->rpubs_lock));
165 : }
166 0 : break;
167 0 : case T_AUTH:
168 : /* FIXME: anything to clean up? */
169 0 : break;
170 0 : case T_WAD:
171 0 : break;
172 : }
173 0 : GNUNET_free (old);
174 : }
175 :
176 :
177 : enum GNUNET_GenericReturnValue
178 55 : 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 55 : GNUNET_assert (0 == strcasecmp (amount->currency,
195 : h->currency));
196 55 : GNUNET_assert (NULL != debit_account);
197 55 : GNUNET_assert (NULL != credit_account);
198 55 : GNUNET_break (0 != strncasecmp ("payto://",
199 : debit_account,
200 : strlen ("payto://")));
201 55 : GNUNET_break (0 != strncasecmp ("payto://",
202 : credit_account,
203 : strlen ("payto://")));
204 55 : url_len = strlen (exchange_base_url);
205 55 : GNUNET_assert (url_len < MAX_URL_LEN);
206 55 : debit_acc = TALER_FAKEBANK_lookup_account_ (h,
207 : debit_account,
208 : debit_account);
209 55 : credit_acc = TALER_FAKEBANK_lookup_account_ (h,
210 : credit_account,
211 : credit_account);
212 55 : if (NULL != request_uid)
213 : {
214 55 : GNUNET_assert (0 ==
215 : pthread_mutex_lock (&h->uuid_map_lock));
216 55 : t = GNUNET_CONTAINER_multihashmap_get (h->uuid_map,
217 : request_uid);
218 55 : if (NULL != t)
219 : {
220 0 : if ( (debit_acc != t->debit_account) ||
221 0 : (credit_acc != t->credit_account) ||
222 0 : (0 != TALER_amount_cmp (amount,
223 0 : &t->amount)) ||
224 0 : (T_DEBIT != t->type) ||
225 0 : (0 != GNUNET_memcmp (subject,
226 : &t->subject.debit.wtid)) )
227 : {
228 : /* Transaction exists, but with different details. */
229 0 : GNUNET_break (0);
230 0 : GNUNET_assert (0 ==
231 : pthread_mutex_unlock (&h->uuid_map_lock));
232 0 : return GNUNET_SYSERR;
233 : }
234 0 : *ret_row_id = t->row_id;
235 0 : *timestamp = t->date;
236 0 : GNUNET_assert (0 ==
237 : pthread_mutex_unlock (&h->uuid_map_lock));
238 0 : return GNUNET_OK;
239 : }
240 55 : GNUNET_assert (0 ==
241 : pthread_mutex_unlock (&h->uuid_map_lock));
242 : }
243 55 : t = GNUNET_new (struct Transaction);
244 55 : t->unchecked = true;
245 55 : t->debit_account = debit_acc;
246 55 : t->credit_account = credit_acc;
247 55 : t->amount = *amount;
248 55 : t->date = GNUNET_TIME_timestamp_get ();
249 55 : if (NULL != timestamp)
250 55 : *timestamp = t->date;
251 55 : t->type = T_DEBIT;
252 55 : GNUNET_memcpy (t->subject.debit.exchange_base_url,
253 : exchange_base_url,
254 : url_len);
255 55 : t->subject.debit.wtid = *subject;
256 55 : if (NULL == request_uid)
257 0 : GNUNET_CRYPTO_random_block (&t->request_uid,
258 : sizeof t->request_uid);
259 : else
260 55 : t->request_uid = *request_uid;
261 55 : TALER_FAKEBANK_transact_ (h,
262 : t);
263 55 : GNUNET_assert (0 ==
264 : pthread_mutex_lock (&h->uuid_map_lock));
265 55 : 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 55 : GNUNET_assert (0 ==
272 : pthread_mutex_unlock (&h->uuid_map_lock));
273 55 : 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 55 : *ret_row_id = t->row_id;
282 55 : TALER_FAKEBANK_notify_transaction_ (h,
283 : t);
284 55 : return GNUNET_OK;
285 : }
|