testing_api_cmd_bank_transfer.c (9653B)
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 do_retry (void *cls) 148 { 149 struct TransferState *fts = cls; 150 151 fts->retry_task = NULL; 152 TALER_TESTING_touch_cmd (fts->is); 153 transfer_run (fts, 154 NULL, 155 fts->is); 156 } 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 confirmation_cb (void *cls, 169 const struct TALER_BANK_TransferResponse *tr) 170 { 171 struct TransferState *fts = cls; 172 struct TALER_TESTING_Interpreter *is = fts->is; 173 174 fts->weh = NULL; 175 if (MHD_HTTP_OK != tr->http_status) 176 { 177 if (0 != fts->do_retry) 178 { 179 fts->do_retry--; 180 if ( (0 == tr->http_status) || 181 (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec) || 182 (MHD_HTTP_INTERNAL_SERVER_ERROR == tr->http_status) ) 183 { 184 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 if (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec) 190 fts->backoff = GNUNET_TIME_UNIT_ZERO; 191 else 192 fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff); 193 TALER_TESTING_inc_tries (fts->is); 194 fts->retry_task 195 = GNUNET_SCHEDULER_add_delayed (fts->backoff, 196 &do_retry, 197 fts); 198 return; 199 } 200 } 201 TALER_TESTING_unexpected_status (is, 202 tr->http_status, 203 MHD_HTTP_OK); 204 return; 205 } 206 207 fts->serial_id = tr->details.ok.row_id; 208 fts->timestamp = tr->details.ok.timestamp; 209 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 transfer_run (void *cls, 222 const struct TALER_TESTING_Command *cmd, 223 struct TALER_TESTING_Interpreter *is) 224 { 225 struct TransferState *fts = cls; 226 void *buf; 227 size_t buf_size; 228 229 (void) cmd; 230 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 TALER_BANK_prepare_transfer (fts->payto_credit_account, 236 &fts->amount, 237 fts->exchange_base_url, 238 &fts->wtid, 239 &buf, 240 &buf_size); 241 fts->is = is; 242 fts->weh 243 = TALER_BANK_transfer ( 244 TALER_TESTING_interpreter_get_context (is), 245 &fts->auth, 246 buf, 247 buf_size, 248 &confirmation_cb, 249 fts); 250 GNUNET_free (buf); 251 if (NULL == fts->weh) 252 { 253 GNUNET_break (0); 254 TALER_TESTING_interpreter_fail (is); 255 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 transfer_cleanup (void *cls, 269 const struct TALER_TESTING_Command *cmd) 270 { 271 struct TransferState *fts = cls; 272 273 if (NULL != fts->weh) 274 { 275 TALER_TESTING_command_incomplete (fts->is, 276 cmd->label); 277 TALER_BANK_transfer_cancel (fts->weh); 278 fts->weh = NULL; 279 } 280 if (NULL != fts->retry_task) 281 { 282 GNUNET_SCHEDULER_cancel (fts->retry_task); 283 fts->retry_task = NULL; 284 } 285 GNUNET_free (fts); 286 } 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 transfer_traits (void *cls, 301 const void **ret, 302 const char *trait, 303 unsigned int index) 304 { 305 struct TransferState *fts = cls; 306 struct TALER_TESTING_Trait traits[] = { 307 TALER_TESTING_make_trait_exchange_url ( 308 fts->exchange_base_url), 309 TALER_TESTING_make_trait_bank_row (&fts->serial_id), 310 TALER_TESTING_make_trait_credit_payto_uri ( 311 &fts->payto_credit_account), 312 TALER_TESTING_make_trait_debit_payto_uri ( 313 &fts->payto_debit_account), 314 TALER_TESTING_make_trait_amount (&fts->amount), 315 TALER_TESTING_make_trait_timestamp (0, &fts->timestamp), 316 TALER_TESTING_make_trait_wtid (&fts->wtid), 317 TALER_TESTING_trait_end () 318 }; 319 320 return TALER_TESTING_get_trait (traits, 321 ret, 322 trait, 323 index); 324 } 325 326 327 struct TALER_TESTING_Command 328 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 fts = GNUNET_new (struct TransferState); 339 fts->account_debit_url = auth->wire_gateway_url; 340 fts->exchange_base_url = exchange_base_url; 341 fts->payto_debit_account = payto_debit_account; 342 fts->payto_credit_account = payto_credit_account; 343 fts->auth = *auth; 344 fts->wtid = *wtid; 345 if (GNUNET_OK != 346 TALER_string_to_amount (amount, 347 &fts->amount)) 348 { 349 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 350 "Failed to parse amount `%s' at %s\n", 351 amount, 352 label); 353 GNUNET_assert (0); 354 } 355 356 { 357 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 return cmd; 366 } 367 } 368 369 370 struct TALER_TESTING_Command 371 TALER_TESTING_cmd_transfer_retry (struct TALER_TESTING_Command cmd) 372 { 373 struct TransferState *fts; 374 375 GNUNET_assert (&transfer_run == cmd.run); 376 fts = cmd.cls; 377 fts->do_retry = NUM_RETRIES; 378 return cmd; 379 } 380 381 382 /* end of testing_api_cmd_bank_transfer.c */