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