testing_api_cmd_bank_transfer.c (9720B)
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 NULL, /* no additional meta data */ 240 &buf, 241 &buf_size); 242 fts->is = is; 243 fts->weh 244 = TALER_BANK_transfer ( 245 TALER_TESTING_interpreter_get_context (is), 246 &fts->auth, 247 buf, 248 buf_size, 249 &confirmation_cb, 250 fts); 251 GNUNET_free (buf); 252 if (NULL == fts->weh) 253 { 254 GNUNET_break (0); 255 TALER_TESTING_interpreter_fail (is); 256 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 transfer_cleanup (void *cls, 270 const struct TALER_TESTING_Command *cmd) 271 { 272 struct TransferState *fts = cls; 273 274 if (NULL != fts->weh) 275 { 276 TALER_TESTING_command_incomplete (fts->is, 277 cmd->label); 278 TALER_BANK_transfer_cancel (fts->weh); 279 fts->weh = NULL; 280 } 281 if (NULL != fts->retry_task) 282 { 283 GNUNET_SCHEDULER_cancel (fts->retry_task); 284 fts->retry_task = NULL; 285 } 286 GNUNET_free (fts); 287 } 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 transfer_traits (void *cls, 302 const void **ret, 303 const char *trait, 304 unsigned int index) 305 { 306 struct TransferState *fts = cls; 307 struct TALER_TESTING_Trait traits[] = { 308 TALER_TESTING_make_trait_exchange_url ( 309 fts->exchange_base_url), 310 TALER_TESTING_make_trait_bank_row (&fts->serial_id), 311 TALER_TESTING_make_trait_credit_payto_uri ( 312 &fts->payto_credit_account), 313 TALER_TESTING_make_trait_debit_payto_uri ( 314 &fts->payto_debit_account), 315 TALER_TESTING_make_trait_amount (&fts->amount), 316 TALER_TESTING_make_trait_timestamp (0, &fts->timestamp), 317 TALER_TESTING_make_trait_wtid (&fts->wtid), 318 TALER_TESTING_trait_end () 319 }; 320 321 return TALER_TESTING_get_trait (traits, 322 ret, 323 trait, 324 index); 325 } 326 327 328 struct TALER_TESTING_Command 329 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 fts = GNUNET_new (struct TransferState); 340 fts->account_debit_url = auth->wire_gateway_url; 341 fts->exchange_base_url = exchange_base_url; 342 fts->payto_debit_account = payto_debit_account; 343 fts->payto_credit_account = payto_credit_account; 344 fts->auth = *auth; 345 fts->wtid = *wtid; 346 if (GNUNET_OK != 347 TALER_string_to_amount (amount, 348 &fts->amount)) 349 { 350 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 351 "Failed to parse amount `%s' at %s\n", 352 amount, 353 label); 354 GNUNET_assert (0); 355 } 356 357 { 358 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 return cmd; 367 } 368 } 369 370 371 struct TALER_TESTING_Command 372 TALER_TESTING_cmd_transfer_retry (struct TALER_TESTING_Command cmd) 373 { 374 struct TransferState *fts; 375 376 GNUNET_assert (&transfer_run == cmd.run); 377 fts = cmd.cls; 378 fts->do_retry = NUM_RETRIES; 379 return cmd; 380 } 381 382 383 /* end of testing_api_cmd_bank_transfer.c */