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/backoff.h"
26 : #include "taler/taler_json_lib.h"
27 : #include <gnunet/gnunet_curl_lib.h>
28 : #include "taler/taler_bank_service.h"
29 : #include "taler/taler_fakebank_lib.h"
30 : #include "taler/taler_signatures.h"
31 : #include "taler/taler_testing_lib.h"
32 :
33 :
34 : /**
35 : * How often do we retry before giving up?
36 : */
37 : #define NUM_RETRIES 5
38 :
39 :
40 : /**
41 : * State for a "transfer" CMD.
42 : */
43 : struct TransferState
44 : {
45 :
46 : /**
47 : * Wire transfer amount.
48 : */
49 : struct TALER_Amount amount;
50 :
51 : /**
52 : * Base URL of the debit account.
53 : */
54 : const char *account_debit_url;
55 :
56 : /**
57 : * Money receiver payto URL.
58 : */
59 : struct TALER_FullPayto payto_debit_account;
60 :
61 : /**
62 : * Money receiver account URL.
63 : */
64 : struct TALER_FullPayto payto_credit_account;
65 :
66 : /**
67 : * Username to use for authentication.
68 : */
69 : struct TALER_BANK_AuthenticationData auth;
70 :
71 : /**
72 : * Base URL of the exchange.
73 : */
74 : const char *exchange_base_url;
75 :
76 : /**
77 : * Wire transfer identifier to use.
78 : */
79 : struct TALER_WireTransferIdentifierRawP wtid;
80 :
81 : /**
82 : * Handle to the pending request at the fakebank.
83 : */
84 : struct TALER_BANK_TransferHandle *weh;
85 :
86 : /**
87 : * Interpreter state.
88 : */
89 : struct TALER_TESTING_Interpreter *is;
90 :
91 : /**
92 : * Set to the wire transfer's unique ID.
93 : */
94 : uint64_t serial_id;
95 :
96 : /**
97 : * Timestamp of the transaction (as returned from the bank).
98 : */
99 : struct GNUNET_TIME_Timestamp timestamp;
100 :
101 : /**
102 : * Configuration filename. Used to get the tip reserve key
103 : * filename (used to obtain a public key to write in the
104 : * transfer subject).
105 : */
106 : const char *config_filename;
107 :
108 : /**
109 : * Task scheduled to try later.
110 : */
111 : struct GNUNET_SCHEDULER_Task *retry_task;
112 :
113 : /**
114 : * How long do we wait until we retry?
115 : */
116 : struct GNUNET_TIME_Relative backoff;
117 :
118 : /**
119 : * Was this command modified via
120 : * #TALER_TESTING_cmd_admin_add_incoming_with_retry to
121 : * enable retries? If so, how often should we still retry?
122 : */
123 : unsigned int do_retry;
124 : };
125 :
126 :
127 : /**
128 : * Run the "transfer" CMD.
129 : *
130 : * @param cls closure.
131 : * @param cmd CMD being run.
132 : * @param is interpreter state.
133 : */
134 : static void
135 : transfer_run (void *cls,
136 : const struct TALER_TESTING_Command *cmd,
137 : struct TALER_TESTING_Interpreter *is);
138 :
139 :
140 : /**
141 : * Task scheduled to re-try #transfer_run.
142 : *
143 : * @param cls a `struct TransferState`
144 : */
145 : static void
146 0 : do_retry (void *cls)
147 : {
148 0 : struct TransferState *fts = cls;
149 :
150 0 : fts->retry_task = NULL;
151 0 : TALER_TESTING_touch_cmd (fts->is);
152 0 : transfer_run (fts,
153 : NULL,
154 : fts->is);
155 0 : }
156 :
157 :
158 : /**
159 : * This callback will process the fakebank response to the wire
160 : * transfer. It just checks whether the HTTP response code is
161 : * acceptable.
162 : *
163 : * @param cls closure with the interpreter state
164 : * @param tr response details
165 : */
166 : static void
167 2 : confirmation_cb (void *cls,
168 : const struct TALER_BANK_TransferResponse *tr)
169 : {
170 2 : struct TransferState *fts = cls;
171 2 : struct TALER_TESTING_Interpreter *is = fts->is;
172 :
173 2 : fts->weh = NULL;
174 2 : if (MHD_HTTP_OK != tr->http_status)
175 : {
176 0 : if (0 != fts->do_retry)
177 : {
178 0 : fts->do_retry--;
179 0 : if ( (0 == tr->http_status) ||
180 0 : (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec) ||
181 0 : (MHD_HTTP_INTERNAL_SERVER_ERROR == tr->http_status) )
182 : {
183 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
184 : "Retrying transfer failed with %u/%d\n",
185 : tr->http_status,
186 : (int) tr->ec);
187 : /* on DB conflicts, do not use backoff */
188 0 : if (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec)
189 0 : fts->backoff = GNUNET_TIME_UNIT_ZERO;
190 : else
191 0 : fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff);
192 0 : TALER_TESTING_inc_tries (fts->is);
193 : fts->retry_task
194 0 : = GNUNET_SCHEDULER_add_delayed (fts->backoff,
195 : &do_retry,
196 : fts);
197 0 : return;
198 : }
199 : }
200 0 : TALER_TESTING_unexpected_status (is,
201 : tr->http_status,
202 : MHD_HTTP_OK);
203 0 : return;
204 : }
205 :
206 2 : fts->serial_id = tr->details.ok.row_id;
207 2 : fts->timestamp = tr->details.ok.timestamp;
208 2 : TALER_TESTING_interpreter_next (is);
209 : }
210 :
211 :
212 : /**
213 : * Run the "transfer" CMD.
214 : *
215 : * @param cls closure.
216 : * @param cmd CMD being run.
217 : * @param is interpreter state.
218 : */
219 : static void
220 2 : transfer_run (void *cls,
221 : const struct TALER_TESTING_Command *cmd,
222 : struct TALER_TESTING_Interpreter *is)
223 : {
224 2 : struct TransferState *fts = cls;
225 : void *buf;
226 : size_t buf_size;
227 :
228 : (void) cmd;
229 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
230 : "Transfer of %s from %s to %s\n",
231 : TALER_amount2s (&fts->amount),
232 : fts->account_debit_url,
233 : fts->payto_credit_account.full_payto);
234 2 : TALER_BANK_prepare_transfer (fts->payto_credit_account,
235 2 : &fts->amount,
236 : fts->exchange_base_url,
237 2 : &fts->wtid,
238 : NULL, /* no additional meta data */
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 */
|