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