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 "taler/platform.h"
26 : #include "taler/backoff.h"
27 : #include "taler/taler_json_lib.h"
28 : #include <gnunet/gnunet_curl_lib.h>
29 : #include "taler/taler_bank_service.h"
30 : #include "taler/taler_fakebank_lib.h"
31 : #include "taler/taler_signatures.h"
32 : #include "taler/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 : NULL, /* no additional meta data */
240 : &buf,
241 : &buf_size);
242 2 : fts->is = is;
243 : fts->weh
244 2 : = TALER_BANK_transfer (
245 : TALER_TESTING_interpreter_get_context (is),
246 2 : &fts->auth,
247 : buf,
248 : buf_size,
249 : &confirmation_cb,
250 : fts);
251 2 : GNUNET_free (buf);
252 2 : if (NULL == fts->weh)
253 : {
254 0 : GNUNET_break (0);
255 0 : TALER_TESTING_interpreter_fail (is);
256 0 : return;
257 : }
258 : }
259 :
260 :
261 : /**
262 : * Free the state of a "fakebank transfer" CMD, and possibly
263 : * cancel a pending operation thereof.
264 : *
265 : * @param cls closure
266 : * @param cmd current CMD being cleaned up.
267 : */
268 : static void
269 2 : transfer_cleanup (void *cls,
270 : const struct TALER_TESTING_Command *cmd)
271 : {
272 2 : struct TransferState *fts = cls;
273 :
274 2 : if (NULL != fts->weh)
275 : {
276 0 : TALER_TESTING_command_incomplete (fts->is,
277 : cmd->label);
278 0 : TALER_BANK_transfer_cancel (fts->weh);
279 0 : fts->weh = NULL;
280 : }
281 2 : if (NULL != fts->retry_task)
282 : {
283 0 : GNUNET_SCHEDULER_cancel (fts->retry_task);
284 0 : fts->retry_task = NULL;
285 : }
286 2 : GNUNET_free (fts);
287 2 : }
288 :
289 :
290 : /**
291 : * Offer internal data from a "fakebank transfer" CMD to other
292 : * commands.
293 : *
294 : * @param cls closure.
295 : * @param[out] ret result
296 : * @param trait name of the trait.
297 : * @param index index number of the object to offer.
298 : * @return #GNUNET_OK on success.
299 : */
300 : static enum GNUNET_GenericReturnValue
301 12 : transfer_traits (void *cls,
302 : const void **ret,
303 : const char *trait,
304 : unsigned int index)
305 : {
306 12 : struct TransferState *fts = cls;
307 : struct TALER_TESTING_Trait traits[] = {
308 12 : TALER_TESTING_make_trait_exchange_url (
309 : fts->exchange_base_url),
310 12 : TALER_TESTING_make_trait_bank_row (&fts->serial_id),
311 12 : TALER_TESTING_make_trait_credit_payto_uri (
312 12 : &fts->payto_credit_account),
313 12 : TALER_TESTING_make_trait_debit_payto_uri (
314 12 : &fts->payto_debit_account),
315 12 : TALER_TESTING_make_trait_amount (&fts->amount),
316 12 : TALER_TESTING_make_trait_timestamp (0, &fts->timestamp),
317 12 : TALER_TESTING_make_trait_wtid (&fts->wtid),
318 12 : TALER_TESTING_trait_end ()
319 : };
320 :
321 12 : return TALER_TESTING_get_trait (traits,
322 : ret,
323 : trait,
324 : index);
325 : }
326 :
327 :
328 : struct TALER_TESTING_Command
329 2 : TALER_TESTING_cmd_transfer (const char *label,
330 : const char *amount,
331 : const struct TALER_BANK_AuthenticationData *auth,
332 : struct TALER_FullPayto payto_debit_account,
333 : struct TALER_FullPayto payto_credit_account,
334 : const struct TALER_WireTransferIdentifierRawP *wtid,
335 : const char *exchange_base_url)
336 : {
337 : struct TransferState *fts;
338 :
339 2 : fts = GNUNET_new (struct TransferState);
340 2 : fts->account_debit_url = auth->wire_gateway_url;
341 2 : fts->exchange_base_url = exchange_base_url;
342 2 : fts->payto_debit_account = payto_debit_account;
343 2 : fts->payto_credit_account = payto_credit_account;
344 2 : fts->auth = *auth;
345 2 : fts->wtid = *wtid;
346 2 : if (GNUNET_OK !=
347 2 : TALER_string_to_amount (amount,
348 : &fts->amount))
349 : {
350 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
351 : "Failed to parse amount `%s' at %s\n",
352 : amount,
353 : label);
354 0 : GNUNET_assert (0);
355 : }
356 :
357 : {
358 2 : struct TALER_TESTING_Command cmd = {
359 : .cls = fts,
360 : .label = label,
361 : .run = &transfer_run,
362 : .cleanup = &transfer_cleanup,
363 : .traits = &transfer_traits
364 : };
365 :
366 2 : return cmd;
367 : }
368 : }
369 :
370 :
371 : struct TALER_TESTING_Command
372 0 : TALER_TESTING_cmd_transfer_retry (struct TALER_TESTING_Command cmd)
373 : {
374 : struct TransferState *fts;
375 :
376 0 : GNUNET_assert (&transfer_run == cmd.run);
377 0 : fts = cmd.cls;
378 0 : fts->do_retry = NUM_RETRIES;
379 0 : return cmd;
380 : }
381 :
382 :
383 : /* end of testing_api_cmd_bank_transfer.c */
|