testing_api_cmd_bank_transfer.c (9686B)
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 "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 do_retry (void *cls) 147 { 148 struct TransferState *fts = cls; 149 150 fts->retry_task = NULL; 151 TALER_TESTING_touch_cmd (fts->is); 152 transfer_run (fts, 153 NULL, 154 fts->is); 155 } 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 confirmation_cb (void *cls, 168 const struct TALER_BANK_TransferResponse *tr) 169 { 170 struct TransferState *fts = cls; 171 struct TALER_TESTING_Interpreter *is = fts->is; 172 173 fts->weh = NULL; 174 if (MHD_HTTP_OK != tr->http_status) 175 { 176 if (0 != fts->do_retry) 177 { 178 fts->do_retry--; 179 if ( (0 == tr->http_status) || 180 (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec) || 181 (MHD_HTTP_INTERNAL_SERVER_ERROR == tr->http_status) ) 182 { 183 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 if (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec) 189 fts->backoff = GNUNET_TIME_UNIT_ZERO; 190 else 191 fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff); 192 TALER_TESTING_inc_tries (fts->is); 193 fts->retry_task 194 = GNUNET_SCHEDULER_add_delayed (fts->backoff, 195 &do_retry, 196 fts); 197 return; 198 } 199 } 200 TALER_TESTING_unexpected_status (is, 201 tr->http_status, 202 MHD_HTTP_OK); 203 return; 204 } 205 206 fts->serial_id = tr->details.ok.row_id; 207 fts->timestamp = tr->details.ok.timestamp; 208 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 transfer_run (void *cls, 221 const struct TALER_TESTING_Command *cmd, 222 struct TALER_TESTING_Interpreter *is) 223 { 224 struct TransferState *fts = cls; 225 void *buf; 226 size_t buf_size; 227 228 (void) cmd; 229 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 TALER_BANK_prepare_transfer (fts->payto_credit_account, 235 &fts->amount, 236 fts->exchange_base_url, 237 &fts->wtid, 238 NULL, /* no additional meta data */ 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 */