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 : char *payto_debit_account;
61 :
62 : /**
63 : * Money receiver account URL.
64 : */
65 : const char *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 1 : do_retry (void *cls)
148 : {
149 1 : struct TransferState *fts = cls;
150 :
151 1 : fts->retry_task = NULL;
152 1 : fts->is->commands[fts->is->ip].last_req_time
153 1 : = GNUNET_TIME_absolute_get ();
154 1 : transfer_run (fts,
155 : NULL,
156 : fts->is);
157 1 : }
158 :
159 :
160 : /**
161 : * This callback will process the fakebank response to the wire
162 : * transfer. It just checks whether the HTTP response code is
163 : * acceptable.
164 : *
165 : * @param cls closure with the interpreter state
166 : * @param http_status HTTP response code, #MHD_HTTP_OK (200) for
167 : * successful status request; 0 if the exchange's reply is
168 : * bogus (fails to follow the protocol)
169 : * @param ec taler-specific error code, #TALER_EC_NONE on success
170 : * @param serial_id unique ID of the wire transfer
171 : * @param timestamp time stamp of the transaction made.
172 : */
173 : static void
174 4 : confirmation_cb (void *cls,
175 : unsigned int http_status,
176 : enum TALER_ErrorCode ec,
177 : uint64_t serial_id,
178 : struct GNUNET_TIME_Timestamp timestamp)
179 : {
180 4 : struct TransferState *fts = cls;
181 4 : struct TALER_TESTING_Interpreter *is = fts->is;
182 :
183 4 : fts->weh = NULL;
184 4 : if (MHD_HTTP_OK != http_status)
185 : {
186 1 : if (0 != fts->do_retry)
187 : {
188 1 : fts->do_retry--;
189 1 : if ( (0 == http_status) ||
190 0 : (TALER_EC_GENERIC_DB_SOFT_FAILURE == ec) ||
191 : (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) )
192 : {
193 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
194 : "Retrying transfer failed with %u/%d\n",
195 : http_status,
196 : (int) ec);
197 : /* on DB conflicts, do not use backoff */
198 1 : if (TALER_EC_GENERIC_DB_SOFT_FAILURE == ec)
199 0 : fts->backoff = GNUNET_TIME_UNIT_ZERO;
200 : else
201 1 : fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff);
202 1 : fts->is->commands[fts->is->ip].num_tries++;
203 : fts->retry_task
204 1 : = GNUNET_SCHEDULER_add_delayed (fts->backoff,
205 : &do_retry,
206 : fts);
207 1 : return;
208 : }
209 : }
210 0 : GNUNET_break (0);
211 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
212 : "Bank returned HTTP status %u/%d\n",
213 : http_status,
214 : (int) ec);
215 0 : TALER_TESTING_interpreter_fail (is);
216 0 : return;
217 : }
218 :
219 3 : fts->serial_id = serial_id;
220 3 : fts->timestamp = timestamp;
221 3 : TALER_TESTING_interpreter_next (is);
222 : }
223 :
224 :
225 : /**
226 : * Run the "transfer" CMD.
227 : *
228 : * @param cls closure.
229 : * @param cmd CMD being run.
230 : * @param is interpreter state.
231 : */
232 : static void
233 4 : transfer_run (void *cls,
234 : const struct TALER_TESTING_Command *cmd,
235 : struct TALER_TESTING_Interpreter *is)
236 : {
237 4 : struct TransferState *fts = cls;
238 : void *buf;
239 : size_t buf_size;
240 :
241 : (void) cmd;
242 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
243 : "Transfer of %s from %s to %s\n",
244 : TALER_amount2s (&fts->amount),
245 : fts->account_debit_url,
246 : fts->payto_credit_account);
247 4 : TALER_BANK_prepare_transfer (fts->payto_credit_account,
248 4 : &fts->amount,
249 : fts->exchange_base_url,
250 4 : &fts->wtid,
251 : &buf,
252 : &buf_size);
253 4 : fts->is = is;
254 : fts->weh
255 4 : = TALER_BANK_transfer (
256 : TALER_TESTING_interpreter_get_context (is),
257 4 : &fts->auth,
258 : buf,
259 : buf_size,
260 : &confirmation_cb,
261 : fts);
262 4 : GNUNET_free (buf);
263 4 : if (NULL == fts->weh)
264 : {
265 0 : GNUNET_break (0);
266 0 : TALER_TESTING_interpreter_fail (is);
267 0 : return;
268 : }
269 : }
270 :
271 :
272 : /**
273 : * Free the state of a "fakebank transfer" CMD, and possibly
274 : * cancel a pending operation thereof.
275 : *
276 : * @param cls closure
277 : * @param cmd current CMD being cleaned up.
278 : */
279 : static void
280 3 : transfer_cleanup (void *cls,
281 : const struct TALER_TESTING_Command *cmd)
282 : {
283 3 : struct TransferState *fts = cls;
284 :
285 3 : if (NULL != fts->weh)
286 : {
287 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
288 : "Command %s did not complete\n",
289 : cmd->label);
290 0 : TALER_BANK_transfer_cancel (fts->weh);
291 0 : fts->weh = NULL;
292 : }
293 3 : if (NULL != fts->retry_task)
294 : {
295 0 : GNUNET_SCHEDULER_cancel (fts->retry_task);
296 0 : fts->retry_task = NULL;
297 : }
298 3 : GNUNET_free (fts->payto_debit_account);
299 3 : GNUNET_free (fts);
300 3 : }
301 :
302 :
303 : /**
304 : * Offer internal data from a "fakebank transfer" CMD to other
305 : * commands.
306 : *
307 : * @param cls closure.
308 : * @param[out] ret result
309 : * @param trait name of the trait.
310 : * @param index index number of the object to offer.
311 : * @return #GNUNET_OK on success.
312 : */
313 : static enum GNUNET_GenericReturnValue
314 12 : transfer_traits (void *cls,
315 : const void **ret,
316 : const char *trait,
317 : unsigned int index)
318 : {
319 12 : struct TransferState *fts = cls;
320 : struct TALER_TESTING_Trait traits[] = {
321 12 : TALER_TESTING_make_trait_exchange_url (
322 : (const char **) &fts->exchange_base_url),
323 12 : TALER_TESTING_make_trait_bank_row (&fts->serial_id),
324 12 : TALER_TESTING_make_trait_credit_payto_uri (
325 : (const char **) &fts->payto_credit_account),
326 12 : TALER_TESTING_make_trait_debit_payto_uri (
327 12 : (const char **) &fts->payto_debit_account),
328 12 : TALER_TESTING_make_trait_amount (&fts->amount),
329 12 : TALER_TESTING_make_trait_timestamp (0, &fts->timestamp),
330 12 : TALER_TESTING_make_trait_wtid (&fts->wtid),
331 12 : TALER_TESTING_trait_end ()
332 : };
333 :
334 12 : return TALER_TESTING_get_trait (traits,
335 : ret,
336 : trait,
337 : index);
338 : }
339 :
340 :
341 : struct TALER_TESTING_Command
342 3 : TALER_TESTING_cmd_transfer (const char *label,
343 : const char *amount,
344 : const struct TALER_BANK_AuthenticationData *auth,
345 : const char *payto_debit_account,
346 : const char *payto_credit_account,
347 : const struct TALER_WireTransferIdentifierRawP *wtid,
348 : const char *exchange_base_url)
349 : {
350 : struct TransferState *fts;
351 :
352 3 : fts = GNUNET_new (struct TransferState);
353 3 : fts->account_debit_url = auth->wire_gateway_url;
354 3 : fts->exchange_base_url = exchange_base_url;
355 3 : fts->payto_debit_account = GNUNET_strdup (payto_debit_account);
356 3 : fts->payto_credit_account = payto_credit_account;
357 3 : fts->auth = *auth;
358 3 : fts->wtid = *wtid;
359 3 : if (GNUNET_OK !=
360 3 : TALER_string_to_amount (amount,
361 : &fts->amount))
362 : {
363 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
364 : "Failed to parse amount `%s' at %s\n",
365 : amount,
366 : label);
367 0 : GNUNET_assert (0);
368 : }
369 :
370 : {
371 3 : struct TALER_TESTING_Command cmd = {
372 : .cls = fts,
373 : .label = label,
374 : .run = &transfer_run,
375 : .cleanup = &transfer_cleanup,
376 : .traits = &transfer_traits
377 : };
378 :
379 3 : return cmd;
380 : }
381 : }
382 :
383 :
384 : struct TALER_TESTING_Command
385 1 : TALER_TESTING_cmd_transfer_retry (struct TALER_TESTING_Command cmd)
386 : {
387 : struct TransferState *fts;
388 :
389 1 : GNUNET_assert (&transfer_run == cmd.run);
390 1 : fts = cmd.cls;
391 1 : fts->do_retry = NUM_RETRIES;
392 1 : return cmd;
393 : }
394 :
395 :
396 : /* end of testing_api_cmd_bank_transfer.c */
|