testing_api_cmd_recoup_refresh.c (12637B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2022 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as 7 published by the Free Software Foundation; either version 3, or 8 (at your 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 13 GNU 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_recoup_refresh.c 21 * @brief Implement the /recoup-refresh test command. 22 * @author Marcello Stanisci 23 */ 24 #include "taler/taler_json_lib.h" 25 #include <gnunet/gnunet_curl_lib.h> 26 struct RecoupRefreshState; 27 #define TALER_EXCHANGE_POST_RECOUP_REFRESH_RESULT_CLOSURE struct \ 28 RecoupRefreshState 29 #include "taler/exchange/post-recoup-refresh.h" 30 #include "taler/taler_testing_lib.h" 31 32 33 /** 34 * State for a "pay back" CMD. 35 */ 36 struct RecoupRefreshState 37 { 38 /** 39 * Expected HTTP status code. 40 */ 41 unsigned int expected_response_code; 42 43 /** 44 * Command that offers a reserve private key, 45 * plus a coin to be paid back. 46 */ 47 const char *coin_reference; 48 49 /** 50 * Entry in the old coin's history generated by this operation. 51 */ 52 struct TALER_EXCHANGE_CoinHistoryEntry che_old; 53 54 /** 55 * Entry in the recouped coin's history generated by this operation. 56 */ 57 struct TALER_EXCHANGE_CoinHistoryEntry che_new; 58 59 /** 60 * Public key of the refunded coin. 61 */ 62 struct TALER_CoinSpendPublicKeyP coin_pub_old; 63 64 /** 65 * Public key of the refunded coin. 66 */ 67 struct TALER_CoinSpendPublicKeyP coin_pub_new; 68 69 /** 70 * Amount to be recouped. 71 */ 72 struct TALER_Amount amount; 73 74 /** 75 * The interpreter state. 76 */ 77 struct TALER_TESTING_Interpreter *is; 78 79 /** 80 * Handle to the ongoing operation. 81 */ 82 struct TALER_EXCHANGE_PostRecoupRefreshHandle *ph; 83 84 /** 85 * NULL if coin was not refreshed, otherwise reference 86 * to the melt operation underlying @a coin_reference. 87 */ 88 const char *melt_reference; 89 90 }; 91 92 93 /** 94 * Check the result of the recoup_refresh request: checks whether 95 * the HTTP response code is good, and that the coin that 96 * was paid back belonged to the right old coin. 97 * 98 * @param rrs closure 99 * @param rrr response details 100 */ 101 static void 102 recoup_refresh_cb (struct RecoupRefreshState *rrs, 103 const struct TALER_EXCHANGE_PostRecoupRefreshResponse *rrr) 104 { 105 const struct TALER_EXCHANGE_HttpResponse *hr = &rrr->hr; 106 struct TALER_TESTING_Interpreter *is = rrs->is; 107 char *cref; 108 unsigned int idx; 109 110 rrs->ph = NULL; 111 if (rrs->expected_response_code != hr->http_status) 112 { 113 TALER_TESTING_unexpected_status (is, 114 hr->http_status, 115 rrs->expected_response_code); 116 return; 117 } 118 119 if (GNUNET_OK != 120 TALER_TESTING_parse_coin_reference ( 121 rrs->coin_reference, 122 &cref, 123 &idx)) 124 { 125 TALER_TESTING_interpreter_fail (is); 126 return; 127 } 128 (void) idx; /* do NOT use! We ignore 'idx', must be 0 for melt! */ 129 130 GNUNET_free (cref); 131 switch (hr->http_status) 132 { 133 case MHD_HTTP_OK: 134 /* check old_coin_pub */ 135 { 136 const struct TALER_TESTING_Command *melt_cmd; 137 const struct TALER_CoinSpendPrivateKeyP *dirty_priv; 138 struct TALER_CoinSpendPublicKeyP oc; 139 140 melt_cmd = TALER_TESTING_interpreter_lookup_command (is, 141 rrs->melt_reference); 142 if (NULL == melt_cmd) 143 { 144 GNUNET_break (0); 145 TALER_TESTING_interpreter_fail (is); 146 return; 147 } 148 if (GNUNET_OK != 149 TALER_TESTING_get_trait_coin_priv (melt_cmd, 150 0, 151 &dirty_priv)) 152 { 153 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 154 "Coin %u not found in command %s\n", 155 0, 156 rrs->melt_reference); 157 GNUNET_break (0); 158 TALER_TESTING_interpreter_fail (is); 159 return; 160 } 161 GNUNET_CRYPTO_eddsa_key_get_public (&dirty_priv->eddsa_priv, 162 &oc.eddsa_pub); 163 if (0 != GNUNET_memcmp (&oc, 164 &rrr->details.ok.old_coin_pub)) 165 { 166 GNUNET_break (0); 167 TALER_TESTING_interpreter_fail (is); 168 return; 169 } 170 } 171 break; 172 case MHD_HTTP_NOT_FOUND: 173 break; 174 case MHD_HTTP_CONFLICT: 175 break; 176 default: 177 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 178 "Unmanaged HTTP status code %u/%d.\n", 179 hr->http_status, 180 (int) hr->ec); 181 break; 182 } 183 TALER_TESTING_interpreter_next (is); 184 } 185 186 187 /** 188 * Run the command. 189 * 190 * @param cls closure. 191 * @param cmd the command to execute. 192 * @param is the interpreter state. 193 */ 194 static void 195 recoup_refresh_run (void *cls, 196 const struct TALER_TESTING_Command *cmd, 197 struct TALER_TESTING_Interpreter *is) 198 { 199 struct RecoupRefreshState *rrs = cls; 200 const struct TALER_TESTING_Command *coin_cmd; 201 const struct TALER_TESTING_Command *melt_cmd; 202 const struct TALER_CoinSpendPrivateKeyP *coin_priv; 203 const struct TALER_CoinSpendPrivateKeyP *coin_priv_old; 204 const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; 205 const struct TALER_DenominationSignature *coin_sig; 206 const struct TALER_PublicRefreshMasterSeedP *rms; 207 const struct TALER_PlanchetMasterSecretP *planchet; 208 const struct TALER_ExchangeBlindingValues *ewv; 209 char *cref; 210 unsigned int idx; 211 struct TALER_DenominationHashP h_denom_pub; 212 213 rrs->is = is; 214 if (GNUNET_OK != 215 TALER_TESTING_parse_coin_reference ( 216 rrs->coin_reference, 217 &cref, 218 &idx)) 219 { 220 TALER_TESTING_interpreter_fail (is); 221 return; 222 } 223 224 coin_cmd = TALER_TESTING_interpreter_lookup_command (is, 225 cref); 226 GNUNET_free (cref); 227 if (NULL == coin_cmd) 228 { 229 GNUNET_break (0); 230 TALER_TESTING_interpreter_fail (is); 231 return; 232 } 233 melt_cmd = TALER_TESTING_interpreter_lookup_command (is, 234 rrs->melt_reference); 235 if (NULL == melt_cmd) 236 { 237 GNUNET_break (0); 238 TALER_TESTING_interpreter_fail (is); 239 return; 240 } 241 if (GNUNET_OK != 242 TALER_TESTING_get_trait_coin_priv (coin_cmd, 243 idx, 244 &coin_priv)) 245 { 246 GNUNET_break (0); 247 TALER_TESTING_interpreter_fail (is); 248 return; 249 } 250 if (GNUNET_OK != 251 TALER_TESTING_get_trait_coin_priv (melt_cmd, 252 0, 253 &coin_priv_old)) 254 { 255 GNUNET_break (0); 256 TALER_TESTING_interpreter_fail (is); 257 return; 258 } 259 GNUNET_CRYPTO_eddsa_key_get_public ( 260 &coin_priv->eddsa_priv, 261 &rrs->coin_pub_new.eddsa_pub); 262 GNUNET_CRYPTO_eddsa_key_get_public ( 263 &coin_priv_old->eddsa_priv, 264 &rrs->coin_pub_old.eddsa_pub); 265 266 if (GNUNET_OK != 267 TALER_TESTING_get_trait_exchange_blinding_values (melt_cmd, 268 idx, 269 &ewv)) 270 { 271 GNUNET_break (0); 272 TALER_TESTING_interpreter_fail (is); 273 return; 274 } 275 if (GNUNET_OK != 276 TALER_TESTING_get_trait_planchet_secrets (coin_cmd, 277 idx, 278 &planchet)) 279 { 280 GNUNET_break (0); 281 TALER_TESTING_interpreter_fail (is); 282 return; 283 } 284 if (GNUNET_OK != 285 TALER_TESTING_get_trait_refresh_seed (melt_cmd, 286 &rms)) 287 { 288 GNUNET_break (0); 289 TALER_TESTING_interpreter_fail (is); 290 return; 291 } 292 if (GNUNET_OK != 293 TALER_TESTING_get_trait_denom_pub (coin_cmd, 294 idx, 295 &denom_pub)) 296 { 297 GNUNET_break (0); 298 TALER_TESTING_interpreter_fail (is); 299 return; 300 } 301 if (GNUNET_OK != 302 TALER_TESTING_get_trait_denom_sig (coin_cmd, 303 idx, 304 &coin_sig)) 305 { 306 GNUNET_break (0); 307 TALER_TESTING_interpreter_fail (is); 308 return; 309 } 310 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 311 "Trying to recoup_refresh denomination '%s'\n", 312 TALER_B2S (&denom_pub->h_key)); 313 rrs->che_old.type 314 = TALER_EXCHANGE_CTT_OLD_COIN_RECOUP; 315 rrs->che_old.amount 316 = rrs->amount; 317 rrs->che_old.details.old_coin_recoup.new_coin_pub 318 = rrs->coin_pub_new; 319 rrs->che_new.type 320 = TALER_EXCHANGE_CTT_RECOUP_REFRESH; 321 rrs->che_new.amount 322 = rrs->amount; 323 rrs->che_new.details.recoup_refresh.old_coin_pub 324 = rrs->coin_pub_old; 325 TALER_planchet_blinding_secret_create ( 326 planchet, 327 ewv, 328 &rrs->che_new.details.recoup_refresh.coin_bks); 329 TALER_denom_pub_hash (&denom_pub->key, 330 &h_denom_pub); 331 TALER_wallet_recoup_refresh_sign ( 332 &h_denom_pub, 333 &rrs->che_new.details.recoup_refresh.coin_bks, 334 coin_priv, 335 &rrs->che_new.details.recoup_refresh.coin_sig); 336 rrs->ph = TALER_EXCHANGE_post_recoup_refresh_create ( 337 TALER_TESTING_interpreter_get_context (is), 338 TALER_TESTING_get_exchange_url (is), 339 TALER_TESTING_get_keys (is), 340 denom_pub, 341 coin_sig, 342 ewv, 343 rms, 344 planchet, 345 idx); 346 GNUNET_assert (NULL != rrs->ph); 347 GNUNET_assert (TALER_EC_NONE == 348 TALER_EXCHANGE_post_recoup_refresh_start (rrs->ph, 349 &recoup_refresh_cb, 350 rrs)); 351 } 352 353 354 /** 355 * Cleanup the "recoup_refresh" CMD state, and possibly cancel 356 * a pending operation thereof. 357 * 358 * @param cls closure. 359 * @param cmd the command which is being cleaned up. 360 */ 361 static void 362 recoup_refresh_cleanup (void *cls, 363 const struct TALER_TESTING_Command *cmd) 364 { 365 struct RecoupRefreshState *rrs = cls; 366 if (NULL != rrs->ph) 367 { 368 TALER_EXCHANGE_post_recoup_refresh_cancel (rrs->ph); 369 rrs->ph = NULL; 370 } 371 GNUNET_free (rrs); 372 } 373 374 375 /** 376 * Offer internal data from a "recoup-refresh" CMD state to other 377 * commands. 378 * 379 * @param cls closure 380 * @param[out] ret result (could be anything) 381 * @param trait name of the trait 382 * @param index index number of the object to offer. 383 * @return #GNUNET_OK on success 384 */ 385 static enum GNUNET_GenericReturnValue 386 recoup_refresh_traits (void *cls, 387 const void **ret, 388 const char *trait, 389 unsigned int index) 390 { 391 struct RecoupRefreshState *rrs = cls; 392 struct TALER_TESTING_Trait traits[] = { 393 TALER_TESTING_make_trait_coin_history (0, 394 &rrs->che_old), 395 TALER_TESTING_make_trait_coin_pub (0, 396 &rrs->coin_pub_old), 397 TALER_TESTING_make_trait_coin_history (1, 398 &rrs->che_new), 399 TALER_TESTING_make_trait_coin_pub (1, 400 &rrs->coin_pub_new), 401 TALER_TESTING_trait_end () 402 }; 403 404 return TALER_TESTING_get_trait (traits, 405 ret, 406 trait, 407 index); 408 } 409 410 411 struct TALER_TESTING_Command 412 TALER_TESTING_cmd_recoup_refresh (const char *label, 413 unsigned int expected_response_code, 414 const char *coin_reference, 415 const char *melt_reference, 416 const char *amount) 417 { 418 struct RecoupRefreshState *rrs; 419 420 rrs = GNUNET_new (struct RecoupRefreshState); 421 rrs->expected_response_code = expected_response_code; 422 rrs->coin_reference = coin_reference; 423 rrs->melt_reference = melt_reference; 424 if (GNUNET_OK != 425 TALER_string_to_amount (amount, 426 &rrs->amount)) 427 { 428 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 429 "Failed to parse amount `%s' at %s\n", 430 amount, 431 label); 432 GNUNET_assert (0); 433 } 434 { 435 struct TALER_TESTING_Command cmd = { 436 .cls = rrs, 437 .label = label, 438 .run = &recoup_refresh_run, 439 .cleanup = &recoup_refresh_cleanup, 440 .traits = &recoup_refresh_traits 441 }; 442 443 return cmd; 444 } 445 }