testing_api_cmd_auditor_deposit_confirmation.c (13881B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2018-2023 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_auditor_deposit_confirmation.c 21 * @brief command for testing /deposit_confirmation. 22 * @author Christian Grothoff 23 */ 24 #include "taler/platform.h" 25 #include "taler/taler_json_lib.h" 26 #include <gnunet/gnunet_curl_lib.h> 27 #include "taler/taler_auditor_service.h" 28 #include "taler/taler_testing_lib.h" 29 #include "taler/taler_signatures.h" 30 #include "taler/backoff.h" 31 32 /** 33 * How long do we wait AT MOST when retrying? 34 */ 35 #define MAX_BACKOFF GNUNET_TIME_relative_multiply ( \ 36 GNUNET_TIME_UNIT_MILLISECONDS, 100) 37 38 /** 39 * How often do we retry before giving up? 40 */ 41 #define NUM_RETRIES 5 42 43 44 /** 45 * State for a "deposit confirmation" CMD. 46 */ 47 struct DepositConfirmationState 48 { 49 50 /** 51 * Reference to any command that is able to provide a deposit. 52 */ 53 const char *deposit_reference; 54 55 /** 56 * What is the deposited amount without the fee (i.e. the 57 * amount we expect in the deposit confirmation)? 58 */ 59 const char *amount_without_fee; 60 61 /** 62 * How many coins were there in the @e deposit_reference? 63 */ 64 unsigned int num_coins; 65 66 /** 67 * DepositConfirmation handle while operation is running. 68 */ 69 struct TALER_AUDITOR_DepositConfirmationHandle *dc; 70 71 /** 72 * Interpreter state. 73 */ 74 struct TALER_TESTING_Interpreter *is; 75 76 /** 77 * Task scheduled to try later. 78 */ 79 struct GNUNET_SCHEDULER_Task *retry_task; 80 81 /** 82 * How long do we wait until we retry? 83 */ 84 struct GNUNET_TIME_Relative backoff; 85 86 /** 87 * Expected HTTP response code. 88 */ 89 unsigned int expected_response_code; 90 91 /** 92 * How often should we retry on (transient) failures? 93 */ 94 unsigned int do_retry; 95 96 }; 97 98 99 /** 100 * Run the command. 101 * 102 * @param cls closure. 103 * @param cmd the command to execute. 104 * @param is the interpreter state. 105 */ 106 static void 107 deposit_confirmation_run (void *cls, 108 const struct TALER_TESTING_Command *cmd, 109 struct TALER_TESTING_Interpreter *is); 110 111 112 /** 113 * Task scheduled to re-try #deposit_confirmation_run. 114 * 115 * @param cls a `struct DepositConfirmationState` 116 */ 117 static void 118 do_retry (void *cls) 119 { 120 struct DepositConfirmationState *dcs = cls; 121 122 dcs->retry_task = NULL; 123 TALER_TESTING_touch_cmd (dcs->is); 124 deposit_confirmation_run (dcs, 125 NULL, 126 dcs->is); 127 } 128 129 130 /** 131 * Callback to analyze the /deposit-confirmation response, just used 132 * to check if the response code is acceptable. 133 * 134 * @param cls closure. 135 * @param dcr response details 136 */ 137 static void 138 deposit_confirmation_cb ( 139 void *cls, 140 const struct TALER_AUDITOR_DepositConfirmationResponse *dcr) 141 { 142 struct DepositConfirmationState *dcs = cls; 143 const struct TALER_AUDITOR_HttpResponse *hr = &dcr->hr; 144 145 dcs->dc = NULL; 146 if (dcs->expected_response_code != hr->http_status) 147 { 148 if (0 != dcs->do_retry) 149 { 150 dcs->do_retry--; 151 if ( (0 == hr->http_status) || 152 (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) || 153 (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) ) 154 { 155 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 156 "Retrying deposit confirmation failed with %u/%d\n", 157 hr->http_status, 158 (int) hr->ec); 159 /* on DB conflicts, do not use backoff */ 160 if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) 161 dcs->backoff = GNUNET_TIME_UNIT_ZERO; 162 else 163 dcs->backoff = GNUNET_TIME_randomized_backoff (dcs->backoff, 164 MAX_BACKOFF); 165 TALER_TESTING_inc_tries (dcs->is); 166 dcs->retry_task = GNUNET_SCHEDULER_add_delayed (dcs->backoff, 167 &do_retry, 168 dcs); 169 return; 170 } 171 } 172 TALER_TESTING_unexpected_status (dcs->is, 173 hr->http_status, 174 dcs->expected_response_code); 175 return; 176 } 177 TALER_TESTING_interpreter_next (dcs->is); 178 } 179 180 181 /** 182 * Run the command. 183 * 184 * @param cls closure. 185 * @param cmd the command to execute. 186 * @param is the interpreter state. 187 */ 188 static void 189 deposit_confirmation_run (void *cls, 190 const struct TALER_TESTING_Command *cmd, 191 struct TALER_TESTING_Interpreter *is) 192 { 193 static struct TALER_ExtensionPolicyHashP no_h_policy; 194 struct DepositConfirmationState *dcs = cls; 195 const struct TALER_TESTING_Command *deposit_cmd; 196 struct TALER_MerchantWireHashP h_wire; 197 struct TALER_PrivateContractHashP h_contract_terms; 198 const struct GNUNET_TIME_Timestamp *exchange_timestamp = NULL; 199 struct GNUNET_TIME_Timestamp timestamp; 200 const struct GNUNET_TIME_Timestamp *wire_deadline; 201 struct GNUNET_TIME_Timestamp refund_deadline 202 = GNUNET_TIME_UNIT_ZERO_TS; 203 struct TALER_Amount amount_without_fee; 204 const struct TALER_Amount *cumulative_total; 205 struct TALER_CoinSpendPublicKeyP coin_pubs[dcs->num_coins]; 206 const struct TALER_CoinSpendPublicKeyP *coin_pubps[dcs->num_coins]; 207 const struct TALER_CoinSpendSignatureP *coin_sigps[dcs->num_coins]; 208 const struct TALER_MerchantPrivateKeyP *merchant_priv; 209 struct TALER_MerchantPublicKeyP merchant_pub; 210 const struct TALER_ExchangePublicKeyP *exchange_pub; 211 const struct TALER_ExchangeSignatureP *exchange_sig; 212 const json_t *wire_details; 213 const json_t *contract_terms; 214 const struct TALER_EXCHANGE_Keys *keys; 215 const struct TALER_EXCHANGE_SigningPublicKey *spk; 216 const char *auditor_url; 217 218 (void) cmd; 219 dcs->is = is; 220 GNUNET_assert (NULL != dcs->deposit_reference); 221 { 222 const struct TALER_TESTING_Command *auditor_cmd; 223 224 auditor_cmd 225 = TALER_TESTING_interpreter_get_command (is, 226 "auditor"); 227 if (NULL == auditor_cmd) 228 { 229 GNUNET_break (0); 230 TALER_TESTING_interpreter_fail (is); 231 return; 232 } 233 if (GNUNET_OK != 234 TALER_TESTING_get_trait_auditor_url (auditor_cmd, 235 &auditor_url)) 236 { 237 GNUNET_break (0); 238 TALER_TESTING_interpreter_fail (is); 239 return; 240 } 241 } 242 deposit_cmd 243 = TALER_TESTING_interpreter_lookup_command (is, 244 dcs->deposit_reference); 245 if (NULL == deposit_cmd) 246 { 247 GNUNET_break (0); 248 TALER_TESTING_interpreter_fail (is); 249 return; 250 } 251 252 GNUNET_assert (GNUNET_OK == 253 TALER_TESTING_get_trait_exchange_pub (deposit_cmd, 254 0, 255 &exchange_pub)); 256 GNUNET_assert (GNUNET_OK == 257 TALER_TESTING_get_trait_exchange_sig (deposit_cmd, 258 0, 259 &exchange_sig)); 260 GNUNET_assert (GNUNET_OK == 261 TALER_TESTING_get_trait_amount (deposit_cmd, 262 &cumulative_total)); 263 GNUNET_assert (GNUNET_OK == 264 TALER_TESTING_get_trait_timestamp (deposit_cmd, 265 0, 266 &exchange_timestamp)); 267 GNUNET_assert (GNUNET_OK == 268 TALER_TESTING_get_trait_wire_deadline (deposit_cmd, 269 0, 270 &wire_deadline)); 271 GNUNET_assert (NULL != exchange_timestamp); 272 keys = TALER_TESTING_get_keys (is); 273 GNUNET_assert (NULL != keys); 274 spk = TALER_EXCHANGE_get_signing_key_info (keys, 275 exchange_pub); 276 277 GNUNET_assert (GNUNET_OK == 278 TALER_TESTING_get_trait_contract_terms (deposit_cmd, 279 &contract_terms)); 280 /* Very unlikely to fail */ 281 GNUNET_assert (NULL != contract_terms); 282 GNUNET_assert (GNUNET_OK == 283 TALER_JSON_contract_hash (contract_terms, 284 &h_contract_terms)); 285 GNUNET_assert (GNUNET_OK == 286 TALER_TESTING_get_trait_wire_details (deposit_cmd, 287 &wire_details)); 288 GNUNET_assert (GNUNET_OK == 289 TALER_JSON_merchant_wire_signature_hash (wire_details, 290 &h_wire)); 291 292 for (unsigned int i = 0; i<dcs->num_coins; i++) 293 { 294 const struct TALER_CoinSpendPrivateKeyP *coin_priv; 295 296 GNUNET_assert (GNUNET_OK == 297 TALER_TESTING_get_trait_coin_priv (deposit_cmd, 298 i, 299 &coin_priv)); 300 GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, 301 &coin_pubs[i].eddsa_pub); 302 coin_pubps[i] = &coin_pubs[i]; 303 GNUNET_assert (GNUNET_OK == 304 TALER_TESTING_get_trait_coin_sig (deposit_cmd, 305 i, 306 &coin_sigps[i])); 307 } 308 GNUNET_assert (GNUNET_OK == 309 TALER_TESTING_get_trait_merchant_priv (deposit_cmd, 310 &merchant_priv)); 311 GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv, 312 &merchant_pub.eddsa_pub); 313 GNUNET_assert (GNUNET_OK == 314 TALER_string_to_amount (dcs->amount_without_fee, 315 &amount_without_fee)); 316 { 317 struct GNUNET_JSON_Specification spec[] = { 318 /* timestamp is mandatory */ 319 GNUNET_JSON_spec_timestamp ("timestamp", 320 ×tamp), 321 GNUNET_JSON_spec_mark_optional ( 322 GNUNET_JSON_spec_timestamp ("refund_deadline", 323 &refund_deadline), 324 NULL), 325 GNUNET_JSON_spec_end () 326 }; 327 328 if (GNUNET_OK != 329 GNUNET_JSON_parse (contract_terms, 330 spec, 331 NULL, NULL)) 332 { 333 GNUNET_break (0); 334 TALER_TESTING_interpreter_fail (is); 335 return; 336 } 337 if (GNUNET_TIME_absolute_is_zero (refund_deadline.abs_time)) 338 refund_deadline = timestamp; 339 } 340 if (-1 == 341 TALER_amount_cmp (cumulative_total, 342 &amount_without_fee)) 343 { 344 345 /* Cumulative must not below the amount we deposited this time */ 346 GNUNET_break (0); 347 TALER_TESTING_interpreter_fail (is); 348 return; 349 } 350 dcs->dc = TALER_AUDITOR_deposit_confirmation ( 351 TALER_TESTING_interpreter_get_context (is), 352 auditor_url, 353 &h_wire, 354 &no_h_policy, 355 &h_contract_terms, 356 *exchange_timestamp, 357 *wire_deadline, 358 refund_deadline, 359 cumulative_total, 360 dcs->num_coins, 361 coin_pubps, 362 coin_sigps, 363 &merchant_pub, 364 exchange_pub, 365 exchange_sig, 366 &keys->master_pub, 367 spk->valid_from, 368 spk->valid_until, 369 spk->valid_legal, 370 &spk->master_sig, 371 &deposit_confirmation_cb, 372 dcs); 373 374 if (NULL == dcs->dc) 375 { 376 GNUNET_break (0); 377 TALER_TESTING_interpreter_fail (is); 378 return; 379 } 380 return; 381 } 382 383 384 /** 385 * Free the state of a "deposit_confirmation" CMD, and possibly cancel a 386 * pending operation thereof. 387 * 388 * @param cls closure, a `struct DepositConfirmationState` 389 * @param cmd the command which is being cleaned up. 390 */ 391 static void 392 deposit_confirmation_cleanup (void *cls, 393 const struct TALER_TESTING_Command *cmd) 394 { 395 struct DepositConfirmationState *dcs = cls; 396 397 if (NULL != dcs->dc) 398 { 399 TALER_TESTING_command_incomplete (dcs->is, 400 cmd->label); 401 TALER_AUDITOR_deposit_confirmation_cancel (dcs->dc); 402 dcs->dc = NULL; 403 } 404 if (NULL != dcs->retry_task) 405 { 406 GNUNET_SCHEDULER_cancel (dcs->retry_task); 407 dcs->retry_task = NULL; 408 } 409 GNUNET_free (dcs); 410 } 411 412 413 struct TALER_TESTING_Command 414 TALER_TESTING_cmd_deposit_confirmation (const char *label, 415 const char *deposit_reference, 416 unsigned int num_coins, 417 const char *amount_without_fee, 418 unsigned int expected_response_code) 419 { 420 struct DepositConfirmationState *dcs; 421 422 dcs = GNUNET_new (struct DepositConfirmationState); 423 dcs->deposit_reference = deposit_reference; 424 dcs->num_coins = num_coins; 425 dcs->amount_without_fee = amount_without_fee; 426 dcs->expected_response_code = expected_response_code; 427 428 { 429 struct TALER_TESTING_Command cmd = { 430 .cls = dcs, 431 .label = label, 432 .run = &deposit_confirmation_run, 433 .cleanup = &deposit_confirmation_cleanup 434 }; 435 436 return cmd; 437 } 438 } 439 440 441 struct TALER_TESTING_Command 442 TALER_TESTING_cmd_deposit_confirmation_with_retry ( 443 struct TALER_TESTING_Command cmd) 444 { 445 struct DepositConfirmationState *dcs; 446 447 GNUNET_assert (&deposit_confirmation_run == cmd.run); 448 dcs = cmd.cls; 449 dcs->do_retry = NUM_RETRIES; 450 return cmd; 451 } 452 453 454 /* end of testing_auditor_api_cmd_deposit_confirmation.c */