Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2018-2021 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it
6 : under the terms of the GNU General Public License as published by
7 : the Free Software Foundation; either version 3, or (at your
8 : option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : 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, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file testing/testing_api_cmd_bank_transfer.c
21 : * @brief implementation of a bank /transfer command
22 : * @author Christian Grothoff
23 : * @author Marcello Stanisci
24 : */
25 : #include "platform.h"
26 : #include "backoff.h"
27 : #include "taler_json_lib.h"
28 : #include <gnunet/gnunet_curl_lib.h>
29 : #include "taler_bank_service.h"
30 : #include "taler_fakebank_lib.h"
31 : #include "taler_signatures.h"
32 : #include "taler_testing_lib.h"
33 :
34 :
35 : /**
36 : * How often do we retry before giving up?
37 : */
38 : #define NUM_RETRIES 5
39 :
40 :
41 : /**
42 : * State for a "transfer" CMD.
43 : */
44 : struct TransferState
45 : {
46 :
47 : /**
48 : * Wire transfer amount.
49 : */
50 : struct TALER_Amount amount;
51 :
52 : /**
53 : * Base URL of the debit account.
54 : */
55 : const char *account_debit_url;
56 :
57 : /**
58 : * Money receiver payto URL.
59 : */
60 : struct TALER_FullPayto payto_debit_account;
61 :
62 : /**
63 : * Money receiver account URL.
64 : */
65 : struct TALER_FullPayto payto_credit_account;
66 :
67 : /**
68 : * Username to use for authentication.
69 : */
70 : struct TALER_BANK_AuthenticationData auth;
71 :
72 : /**
73 : * Base URL of the exchange.
74 : */
75 : const char *exchange_base_url;
76 :
77 : /**
78 : * Wire transfer identifier to use.
79 : */
80 : struct TALER_WireTransferIdentifierRawP wtid;
81 :
82 : /**
83 : * Handle to the pending request at the fakebank.
84 : */
85 : struct TALER_BANK_TransferHandle *weh;
86 :
87 : /**
88 : * Interpreter state.
89 : */
90 : struct TALER_TESTING_Interpreter *is;
91 :
92 : /**
93 : * Set to the wire transfer's unique ID.
94 : */
95 : uint64_t serial_id;
96 :
97 : /**
98 : * Timestamp of the transaction (as returned from the bank).
99 : */
100 : struct GNUNET_TIME_Timestamp timestamp;
101 :
102 : /**
103 : * Configuration filename. Used to get the tip reserve key
104 : * filename (used to obtain a public key to write in the
105 : * transfer subject).
106 : */
107 : const char *config_filename;
108 :
109 : /**
110 : * Task scheduled to try later.
111 : */
112 : struct GNUNET_SCHEDULER_Task *retry_task;
113 :
114 : /**
115 : * How long do we wait until we retry?
116 : */
117 : struct GNUNET_TIME_Relative backoff;
118 :
119 : /**
120 : * Was this command modified via
121 : * #TALER_TESTING_cmd_admin_add_incoming_with_retry to
122 : * enable retries? If so, how often should we still retry?
123 : */
124 : unsigned int do_retry;
125 : };
126 :
127 :
128 : /**
129 : * Run the "transfer" CMD.
130 : *
131 : * @param cls closure.
132 : * @param cmd CMD being run.
133 : * @param is interpreter state.
134 : */
135 : static void
136 : transfer_run (void *cls,
137 : const struct TALER_TESTING_Command *cmd,
138 : struct TALER_TESTING_Interpreter *is);
139 :
140 :
141 : /**
142 : * Task scheduled to re-try #transfer_run.
143 : *
144 : * @param cls a `struct TransferState`
145 : */
146 : static void
147 0 : do_retry (void *cls)
148 : {
149 0 : struct TransferState *fts = cls;
150 :
151 0 : fts->retry_task = NULL;
152 0 : TALER_TESTING_touch_cmd (fts->is);
153 0 : transfer_run (fts,
154 : NULL,
155 : fts->is);
156 0 : }
157 :
158 :
159 : /**
160 : * This callback will process the fakebank response to the wire
161 : * transfer. It just checks whether the HTTP response code is
162 : * acceptable.
163 : *
164 : * @param cls closure with the interpreter state
165 : * @param tr response details
166 : */
167 : static void
168 2 : confirmation_cb (void *cls,
169 : const struct TALER_BANK_TransferResponse *tr)
170 : {
171 2 : struct TransferState *fts = cls;
172 2 : struct TALER_TESTING_Interpreter *is = fts->is;
173 :
174 2 : fts->weh = NULL;
175 2 : if (MHD_HTTP_OK != tr->http_status)
176 : {
177 0 : if (0 != fts->do_retry)
178 : {
179 0 : fts->do_retry--;
180 0 : if ( (0 == tr->http_status) ||
181 0 : (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec) ||
182 0 : (MHD_HTTP_INTERNAL_SERVER_ERROR == tr->http_status) )
183 : {
184 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
185 : "Retrying transfer failed with %u/%d\n",
186 : tr->http_status,
187 : (int) tr->ec);
188 : /* on DB conflicts, do not use backoff */
189 0 : if (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec)
190 0 : fts->backoff = GNUNET_TIME_UNIT_ZERO;
191 : else
192 0 : fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff);
193 0 : TALER_TESTING_inc_tries (fts->is);
194 : fts->retry_task
195 0 : = GNUNET_SCHEDULER_add_delayed (fts->backoff,
196 : &do_retry,
197 : fts);
198 0 : return;
199 : }
200 : }
201 0 : TALER_TESTING_unexpected_status (is,
202 : tr->http_status,
203 : MHD_HTTP_OK);
204 0 : return;
205 : }
206 :
207 2 : fts->serial_id = tr->details.ok.row_id;
208 2 : fts->timestamp = tr->details.ok.timestamp;
209 2 : TALER_TESTING_interpreter_next (is);
210 : }
211 :
212 :
213 : /**
214 : * Run the "transfer" CMD.
215 : *
216 : * @param cls closure.
217 : * @param cmd CMD being run.
218 : * @param is interpreter state.
219 : */
220 : static void
221 2 : transfer_run (void *cls,
222 : const struct TALER_TESTING_Command *cmd,
223 : struct TALER_TESTING_Interpreter *is)
224 : {
225 2 : struct TransferState *fts = cls;
226 : void *buf;
227 : size_t buf_size;
228 :
229 : (void) cmd;
230 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
231 : "Transfer of %s from %s to %s\n",
232 : TALER_amount2s (&fts->amount),
233 : fts->account_debit_url,
234 : fts->payto_credit_account.full_payto);
235 2 : TALER_BANK_prepare_transfer (fts->payto_credit_account,
236 2 : &fts->amount,
237 : fts->exchange_base_url,
238 2 : &fts->wtid,
239 : &buf,
240 : &buf_size);
241 2 : fts->is = is;
242 : fts->weh
243 2 : = TALER_BANK_transfer (
244 : TALER_TESTING_interpreter_get_context (is),
245 2 : &fts->auth,
246 : buf,
247 : buf_size,
248 : &confirmation_cb,
249 : fts);
250 2 : GNUNET_free (buf);
251 2 : if (NULL == fts->weh)
252 : {
253 0 : GNUNET_break (0);
254 0 : TALER_TESTING_interpreter_fail (is);
255 0 : return;
256 : }
257 : }
258 :
259 :
260 : /**
261 : * Free the state of a "fakebank transfer" CMD, and possibly
262 : * cancel a pending operation thereof.
263 : *
264 : * @param cls closure
265 : * @param cmd current CMD being cleaned up.
266 : */
267 : static void
268 2 : transfer_cleanup (void *cls,
269 : const struct TALER_TESTING_Command *cmd)
270 : {
271 2 : struct TransferState *fts = cls;
272 :
273 2 : if (NULL != fts->weh)
274 : {
275 0 : TALER_TESTING_command_incomplete (fts->is,
276 : cmd->label);
277 0 : TALER_BANK_transfer_cancel (fts->weh);
278 0 : fts->weh = NULL;
279 : }
280 2 : if (NULL != fts->retry_task)
281 : {
282 0 : GNUNET_SCHEDULER_cancel (fts->retry_task);
283 0 : fts->retry_task = NULL;
284 : }
285 2 : GNUNET_free (fts);
286 2 : }
287 :
288 :
289 : /**
290 : * Offer internal data from a "fakebank transfer" CMD to other
291 : * commands.
292 : *
293 : * @param cls closure.
294 : * @param[out] ret result
295 : * @param trait name of the trait.
296 : * @param index index number of the object to offer.
297 : * @return #GNUNET_OK on success.
298 : */
299 : static enum GNUNET_GenericReturnValue
300 12 : transfer_traits (void *cls,
301 : const void **ret,
302 : const char *trait,
303 : unsigned int index)
304 : {
305 12 : struct TransferState *fts = cls;
306 : struct TALER_TESTING_Trait traits[] = {
307 12 : TALER_TESTING_make_trait_exchange_url (
308 : fts->exchange_base_url),
309 12 : TALER_TESTING_make_trait_bank_row (&fts->serial_id),
310 12 : TALER_TESTING_make_trait_credit_payto_uri (
311 12 : &fts->payto_credit_account),
312 12 : TALER_TESTING_make_trait_debit_payto_uri (
313 12 : &fts->payto_debit_account),
314 12 : TALER_TESTING_make_trait_amount (&fts->amount),
315 12 : TALER_TESTING_make_trait_timestamp (0, &fts->timestamp),
316 12 : TALER_TESTING_make_trait_wtid (&fts->wtid),
317 12 : TALER_TESTING_trait_end ()
318 : };
319 :
320 12 : return TALER_TESTING_get_trait (traits,
321 : ret,
322 : trait,
323 : index);
324 : }
325 :
326 :
327 : struct TALER_TESTING_Command
328 2 : TALER_TESTING_cmd_transfer (const char *label,
329 : const char *amount,
330 : const struct TALER_BANK_AuthenticationData *auth,
331 : struct TALER_FullPayto payto_debit_account,
332 : struct TALER_FullPayto payto_credit_account,
333 : const struct TALER_WireTransferIdentifierRawP *wtid,
334 : const char *exchange_base_url)
335 : {
336 : struct TransferState *fts;
337 :
338 2 : fts = GNUNET_new (struct TransferState);
339 2 : fts->account_debit_url = auth->wire_gateway_url;
340 2 : fts->exchange_base_url = exchange_base_url;
341 2 : fts->payto_debit_account = payto_debit_account;
342 2 : fts->payto_credit_account = payto_credit_account;
343 2 : fts->auth = *auth;
344 2 : fts->wtid = *wtid;
345 2 : if (GNUNET_OK !=
346 2 : TALER_string_to_amount (amount,
347 : &fts->amount))
348 : {
349 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
350 : "Failed to parse amount `%s' at %s\n",
351 : amount,
352 : label);
353 0 : GNUNET_assert (0);
354 : }
355 :
356 : {
357 2 : struct TALER_TESTING_Command cmd = {
358 : .cls = fts,
359 : .label = label,
360 : .run = &transfer_run,
361 : .cleanup = &transfer_cleanup,
362 : .traits = &transfer_traits
363 : };
364 :
365 2 : return cmd;
366 : }
367 : }
368 :
369 :
370 : struct TALER_TESTING_Command
371 0 : TALER_TESTING_cmd_transfer_retry (struct TALER_TESTING_Command cmd)
372 : {
373 : struct TransferState *fts;
374 :
375 0 : GNUNET_assert (&transfer_run == cmd.run);
376 0 : fts = cmd.cls;
377 0 : fts->do_retry = NUM_RETRIES;
378 0 : return cmd;
379 : }
380 :
381 :
382 : /* end of testing_api_cmd_bank_transfer.c */
|