taler-exchange-httpd_melt_v27.c (52308B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler-exchange-httpd_melt_v27.c 18 * @brief Handle /melt requests 19 * @note This endpoint is active since v27 of the protocol API 20 * @author Özgür Kesim 21 */ 22 23 #include "taler/platform.h" 24 #include <gnunet/gnunet_util_lib.h> 25 #include <jansson.h> 26 #include "taler-exchange-httpd.h" 27 #include "taler/taler_json_lib.h" 28 #include "taler/taler_mhd_lib.h" 29 #include "taler-exchange-httpd_melt_v27.h" 30 #include "taler-exchange-httpd_responses.h" 31 #include "taler-exchange-httpd_keys.h" 32 #include "taler/taler_util.h" 33 34 /** 35 * The different type of errors that might occur, sorted by name. 36 * Some of them require idempotency checks, which are marked 37 * in @e idempotency_check_required below. 38 */ 39 enum MeltError 40 { 41 MELT_ERROR_NONE = 0, 42 MELT_ERROR_AGE_RESTRICTION_COMMITMENT_INVALID, 43 MELT_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION, 44 MELT_ERROR_AMOUNT_OVERFLOW, 45 MELT_ERROR_AMOUNT_PLUS_FEE_OVERFLOW, 46 MELT_ERROR_AMOUNT_WITH_FEE_INCORRECT, 47 MELT_ERROR_BLINDING_SEED_REQUIRED, 48 MELT_ERROR_COIN_CIPHER_MISMATCH, 49 MELT_COIN_CONFLICTING_DENOMINATION_KEY, 50 MELT_ERROR_COIN_EXPIRED_NO_ZOMBIE, 51 MELT_ERROR_COIN_SIGNATURE_INVALID, 52 MELT_ERROR_COIN_UNKNOWN, 53 MELT_ERROR_CONFIRMATION_SIGN, 54 MELT_ERROR_CRYPTO_HELPER, 55 MELT_ERROR_DB_FETCH_FAILED, 56 MELT_ERROR_DB_INVARIANT_FAILURE, 57 MELT_ERROR_DB_MAKE_COIN_KNOW_FAILURE, 58 MELT_ERROR_DB_PREFLIGHT_FAILURE, 59 MELT_ERROR_DENOMINATION_EXPIRED, 60 MELT_ERROR_DENOMINATION_KEY_UNKNOWN, 61 MELT_ERROR_DENOMINATION_REVOKED, 62 MELT_ERROR_DENOMINATION_SIGN, 63 MELT_ERROR_DENOMINATION_SIGNATURE_INVALID, 64 MELT_ERROR_DENOMINATION_VALIDITY_IN_FUTURE, 65 MELT_ERROR_IDEMPOTENT_PLANCHET, 66 MELT_ERROR_INSUFFICIENT_FUNDS, 67 MELT_ERROR_KEYS_MISSING, 68 MELT_ERROR_FEES_EXCEED_CONTRIBUTION, 69 MELT_ERROR_NONCE_RESUSE, 70 MELT_ERROR_REQUEST_PARAMETER_MALFORMED, 71 }; 72 73 /** 74 * With the bits set in this value will be mark the errors 75 * that require a check for idempotency before actually 76 * returning an error. 77 */ 78 static const uint64_t idempotency_check_required = 79 0 80 | (1 << MELT_ERROR_DENOMINATION_EXPIRED) 81 | (1 << MELT_ERROR_DENOMINATION_KEY_UNKNOWN) 82 | (1 << MELT_ERROR_DENOMINATION_REVOKED) 83 | (1 << MELT_ERROR_INSUFFICIENT_FUNDS) /* TODO: is this still correct? Compare exchange_do_refresh.sql */ 84 | (1 << MELT_ERROR_KEYS_MISSING); 85 86 #define IDEMPOTENCY_CHECK_REQUIRED(error) \ 87 (0 != (idempotency_check_required & (1 << (error)))) 88 89 /** 90 * Context for a /melt request 91 */ 92 struct MeltContext 93 { 94 95 /** 96 * This struct is kept in a DLL. 97 */ 98 struct MeltContext *prev; 99 struct MeltContext *next; 100 101 /** 102 * Processing phase we are in. 103 * The ordering here partially matters, as we progress through 104 * them by incrementing the phase in the happy path. 105 */ 106 enum MeltPhase 107 { 108 MELT_PHASE_PARSE, 109 MELT_PHASE_CHECK_MELT_VALID, 110 MELT_PHASE_CHECK_KEYS, 111 MELT_PHASE_CHECK_COIN_SIGNATURE, 112 MELT_PHASE_PREPARE_TRANSACTION, 113 MELT_PHASE_RUN_TRANSACTION, 114 MELT_PHASE_GENERATE_REPLY_SUCCESS, 115 MELT_PHASE_GENERATE_REPLY_ERROR, 116 MELT_PHASE_RETURN_NO, 117 MELT_PHASE_RETURN_YES, 118 } phase; 119 120 121 /** 122 * Request context 123 */ 124 const struct TEH_RequestContext *rc; 125 126 /** 127 * Current time for the DB transaction. 128 */ 129 struct GNUNET_TIME_Timestamp now; 130 131 /** 132 * The current key state 133 */ 134 struct TEH_KeyStateHandle *ksh; 135 136 /** 137 * The melted coin's denomination key 138 */ 139 struct TEH_DenominationKey *melted_coin_denom; 140 141 /** 142 * Set to true if this coin's denomination was revoked and the operation 143 * is thus only allowed for zombie coins where the transaction 144 * history includes a #TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP. 145 */ 146 bool zombie_required; 147 148 /** 149 * We already checked and noticed that the coin is known. Hence we 150 * can skip the "ensure_coin_known" step of the transaction. 151 */ 152 bool coin_is_dirty; 153 154 /** 155 * UUID of the coin in the known_coins table. 156 */ 157 uint64_t known_coin_id; 158 159 /** 160 * Captures all parameters provided in the JSON request 161 */ 162 struct 163 { 164 165 /** 166 * All fields (from the request or computed) 167 * that we persist in the database. 168 */ 169 struct TALER_EXCHANGEDB_Refresh_v27 refresh; 170 171 /** 172 * In some error cases we check for idempotency. 173 * If we find an entry in the database, we mark this here. 174 */ 175 bool is_idempotent; 176 177 /** 178 * In some error conditions the request is checked 179 * for idempotency and the result from the database 180 * is stored here. 181 */ 182 struct TALER_EXCHANGEDB_Refresh_v27 refresh_idem; 183 184 /** 185 * True if @e blinding_seed is missing in the request 186 */ 187 bool no_blinding_seed; 188 189 /** 190 * Array @e persis.num_coins of hashes of the public keys 191 * of the denominations to refresh. 192 */ 193 struct TALER_DenominationHashP *denoms_h; 194 195 /** 196 * Array of @e num_planchets coin planchets, arranged 197 * in runs of @e num_coins coins, [0..num_coins)..[0..num_coins), 198 * one for each kappa value. 199 */ 200 struct TALER_BlindedPlanchet *planchets[TALER_CNC_KAPPA]; 201 202 /** 203 * #TALER_CNC_KAPPA hashes of the batches of @e num_coins coins. 204 */ 205 struct TALER_KappaHashBlindedPlanchetsP kappa_planchets_h; 206 207 /** 208 * Array @e withdraw.num_r_pubs of indices into @e denoms_h 209 * of CS denominations. 210 */ 211 uint32_t *cs_indices; 212 213 /** 214 * Total (over all coins) amount (excluding fee) committed for the refresh 215 */ 216 struct TALER_Amount amount; 217 218 } request; 219 220 /** 221 * Errors occurring during evaluation of the request are captured in this 222 * struct. In phase WITHDRAW_PHASE_GENERATE_REPLY_ERROR an appropriate error 223 * message is prepared and sent to the client. 224 */ 225 struct 226 { 227 /* The (internal) error code */ 228 enum MeltError code; 229 230 /** 231 * Some errors require details to be sent to the client. 232 * These are captured in this union. 233 * Each field is named according to the error that is using it, except 234 * commented otherwise. 235 */ 236 union 237 { 238 const char *request_parameter_malformed; 239 240 /** 241 * For all errors related to a particular denomination, i.e. 242 * #MELT_ERROR_DENOMINATION_KEY_UNKNOWN, 243 * #MELT_ERROR_DENOMINATION_EXPIRED, 244 * #MELT_ERROR_DENOMINATION_VALIDITY_IN_FUTURE, 245 * #MELT_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION, 246 * we use this one field. 247 */ 248 struct TALER_DenominationHashP denom_h; 249 250 const char *db_fetch_context; 251 252 enum TALER_ErrorCode ec_confirmation_sign; 253 254 enum TALER_ErrorCode ec_denomination_sign; 255 256 /* remaining value of the coin */ 257 struct TALER_Amount insufficient_funds; 258 259 } details; 260 } error; 261 }; 262 263 /** 264 * The following macros set the given error code, 265 * set the phase to Melt_PHASE_GENERATE_REPLY_ERROR, 266 * and optionally set the given field (with an optionally given value). 267 */ 268 #define SET_ERROR(mc, ec) \ 269 do \ 270 { GNUNET_static_assert (MELT_ERROR_NONE != ec); \ 271 (mc)->error.code = (ec); \ 272 (mc)->phase = MELT_PHASE_GENERATE_REPLY_ERROR; } while (0) 273 274 #define SET_ERROR_WITH_FIELD(mc, ec, field) \ 275 do \ 276 { GNUNET_static_assert (MELT_ERROR_NONE != ec); \ 277 (mc)->error.code = (ec); \ 278 (mc)->error.details.field = (field); \ 279 (mc)->phase = MELT_PHASE_GENERATE_REPLY_ERROR; } while (0) 280 281 #define SET_ERROR_WITH_DETAIL(mc, ec, field, value) \ 282 do \ 283 { GNUNET_static_assert (MELT_ERROR_NONE != ec); \ 284 (mc)->error.code = (ec); \ 285 (mc)->error.details.field = (value); \ 286 (mc)->phase = MELT_PHASE_GENERATE_REPLY_ERROR; } while (0) 287 288 289 /** 290 * All melt context is kept in a DLL. 291 */ 292 static struct MeltContext *mc_head; 293 static struct MeltContext *mc_tail; 294 295 void 296 TEH_melt_v27_cleanup () 297 { 298 struct MeltContext *mc; 299 300 while (NULL != (mc = mc_head)) 301 { 302 GNUNET_CONTAINER_DLL_remove (mc_head, 303 mc_tail, 304 mc); 305 MHD_resume_connection (mc->rc->connection); 306 } 307 } 308 309 310 /** 311 * Terminate the main loop by returning the final result. 312 * 313 * @param[in,out] mc context to update phase for 314 * @param mres MHD status to return 315 */ 316 static void 317 finish_loop (struct MeltContext *mc, 318 MHD_RESULT mres) 319 { 320 mc->phase = (MHD_YES == mres) 321 ? MELT_PHASE_RETURN_YES 322 : MELT_PHASE_RETURN_NO; 323 } 324 325 326 /** 327 * Free information in @a re, but not @a re itself. 328 * 329 * @param[in] re refresh data to free 330 */ 331 static void 332 free_refresh (struct TALER_EXCHANGEDB_Refresh_v27 *re) 333 { 334 if (NULL != re->denom_sigs) 335 { 336 for (size_t i = 0; i<re->num_coins; i++) 337 TALER_blinded_denom_sig_free (&re->denom_sigs[i]); 338 GNUNET_free (re->denom_sigs); 339 } 340 GNUNET_free (re->cs_r_values); 341 GNUNET_free (re->denom_serials); 342 GNUNET_free (re->denom_pub_hashes); 343 TALER_denom_sig_free (&re->coin.denom_sig); 344 } 345 346 347 /** 348 * Cleanup routine for melt request. 349 * The function is called upon completion of the request 350 * that should clean up @a rh_ctx. 351 * 352 * @param rc request context to clean up 353 */ 354 static void 355 clean_melt_rc (struct TEH_RequestContext *rc) 356 { 357 struct MeltContext *mc = rc->rh_ctx; 358 359 GNUNET_free (mc->request.denoms_h); 360 for (uint8_t k = 0; k<TALER_CNC_KAPPA; k++) 361 { 362 for (size_t i = 0; i<mc->request.refresh.num_coins; i++) 363 TALER_blinded_planchet_free (&mc->request.planchets[k][i]); 364 GNUNET_free (mc->request.planchets[k]); 365 } 366 free_refresh (&mc->request.refresh); 367 if (mc->request.is_idempotent) 368 { 369 free_refresh (&mc->request.refresh_idem); 370 } 371 GNUNET_free (mc->request.cs_indices); 372 GNUNET_free (mc); 373 } 374 375 376 /** 377 * Creates a new context for the incoming melt request 378 * 379 * @param mc melt request context 380 * @param root json body of the request 381 */ 382 static void 383 phase_parse_request ( 384 struct MeltContext *mc, 385 const json_t *root) 386 { 387 const json_t *j_denoms_h; 388 const json_t *j_coin_evs; 389 enum GNUNET_GenericReturnValue res; 390 struct GNUNET_JSON_Specification spec[] = { 391 GNUNET_JSON_spec_fixed_auto ("old_coin_pub", 392 &mc->request.refresh.coin.coin_pub), 393 GNUNET_JSON_spec_fixed_auto ("old_denom_pub_h", 394 &mc->request.refresh.coin.denom_pub_hash), 395 GNUNET_JSON_spec_mark_optional ( 396 GNUNET_JSON_spec_fixed_auto ("old_age_commitment_h", 397 &mc->request.refresh.coin.h_age_commitment), 398 &mc->request.refresh.coin.no_age_commitment), 399 TALER_JSON_spec_denom_sig ("old_denom_sig", 400 &mc->request.refresh.coin.denom_sig), 401 GNUNET_JSON_spec_fixed_auto ("refresh_seed", 402 &mc->request.refresh.refresh_seed), 403 GNUNET_JSON_spec_mark_optional ( 404 GNUNET_JSON_spec_fixed_auto ("blinding_seed", 405 &mc->request.refresh.blinding_seed), 406 &mc->request.refresh.no_blinding_seed), 407 TALER_JSON_spec_amount ("value_with_fee", 408 TEH_currency, 409 &mc->request.refresh.amount_with_fee), 410 GNUNET_JSON_spec_array_const ("denoms_h", 411 &j_denoms_h), 412 GNUNET_JSON_spec_array_const ("coin_evs", 413 &j_coin_evs), 414 GNUNET_JSON_spec_fixed_auto ("confirm_sig", 415 &mc->request.refresh.coin_sig), 416 GNUNET_JSON_spec_end () 417 }; 418 419 res = TALER_MHD_parse_json_data (mc->rc->connection, 420 root, 421 spec); 422 if (GNUNET_OK != res) 423 { 424 GNUNET_break_op (0); 425 mc->phase = (GNUNET_NO == res) 426 ? MELT_PHASE_RETURN_YES 427 : MELT_PHASE_RETURN_NO; 428 return; 429 } 430 431 /* validate array size */ 432 GNUNET_static_assert ( 433 TALER_MAX_COINS < INT_MAX / TALER_CNC_KAPPA); 434 435 mc->request.refresh.num_coins = json_array_size (j_denoms_h); 436 if (0 == mc->request.refresh.num_coins) 437 { 438 GNUNET_break_op (0); 439 SET_ERROR_WITH_DETAIL (mc, 440 MELT_ERROR_REQUEST_PARAMETER_MALFORMED, 441 request_parameter_malformed, 442 "denoms_h must not be empty"); 443 return; 444 } 445 else if (TALER_MAX_COINS < mc->request.refresh.num_coins) 446 { 447 /** 448 * The wallet had committed to more than the maximum coins allowed, the 449 * reserve has been charged, but now the user can not melt any money 450 * from it. Note that the user can't get their money back in this case! 451 */ 452 GNUNET_break_op (0); 453 SET_ERROR_WITH_DETAIL (mc, 454 MELT_ERROR_REQUEST_PARAMETER_MALFORMED, 455 request_parameter_malformed, 456 "maximum number of coins that can be refreshed has been exceeded"); 457 return; 458 } 459 else if (TALER_CNC_KAPPA != json_array_size (j_coin_evs)) 460 { 461 GNUNET_break_op (0); 462 SET_ERROR_WITH_DETAIL (mc, 463 MELT_ERROR_REQUEST_PARAMETER_MALFORMED, 464 request_parameter_malformed, 465 "coin_evs must be an array of length "TALER_CNC_KAPPA_STR); 466 return; 467 } 468 469 /* Extract the denomination hashes */ 470 { 471 size_t idx; 472 json_t *value; 473 474 mc->request.denoms_h 475 = GNUNET_new_array (mc->request.refresh.num_coins, 476 struct TALER_DenominationHashP); 477 478 json_array_foreach (j_denoms_h, idx, value) { 479 struct GNUNET_JSON_Specification ispec[] = { 480 GNUNET_JSON_spec_fixed_auto (NULL, 481 &mc->request.denoms_h[idx]), 482 GNUNET_JSON_spec_end () 483 }; 484 485 res = TALER_MHD_parse_json_data (mc->rc->connection, 486 value, 487 ispec); 488 if (GNUNET_YES != res) 489 { 490 GNUNET_break_op (0); 491 mc->phase = (GNUNET_NO == res) 492 ? MELT_PHASE_RETURN_YES 493 : MELT_PHASE_RETURN_NO; 494 return; 495 } 496 } 497 } 498 499 /* Calculate the hash over the blinded coin envelopes */ 500 for (size_t k = 0; k<TALER_CNC_KAPPA; k++) 501 { 502 mc->request.planchets[k] = 503 GNUNET_new_array (mc->request.refresh.num_coins, 504 struct TALER_BlindedPlanchet); 505 } 506 507 /* Parse blinded envelopes. */ 508 { 509 json_t *j_kappa_planchets; 510 size_t kappa; 511 struct GNUNET_HashContext *ctx; 512 513 /* ctx to calculate the planchet_h */ 514 ctx = GNUNET_CRYPTO_hash_context_start (); 515 GNUNET_assert (NULL != ctx); 516 517 json_array_foreach (j_coin_evs, kappa, j_kappa_planchets) 518 { 519 json_t *j_cev; 520 size_t idx; 521 522 if (mc->request.refresh.num_coins != json_array_size (j_kappa_planchets)) 523 { 524 GNUNET_break_op (0); 525 SET_ERROR_WITH_DETAIL (mc, 526 MELT_ERROR_REQUEST_PARAMETER_MALFORMED, 527 request_parameter_malformed, 528 "coin_evs[] size"); 529 return; 530 } 531 532 json_array_foreach (j_kappa_planchets, idx, j_cev) 533 { 534 /* Now parse the individual envelopes and calculate the hash of 535 * the commitment along the way. */ 536 struct GNUNET_JSON_Specification kspec[] = { 537 TALER_JSON_spec_blinded_planchet (NULL, 538 &mc->request.planchets[kappa][idx]), 539 GNUNET_JSON_spec_end () 540 }; 541 542 res = TALER_MHD_parse_json_data (mc->rc->connection, 543 j_cev, 544 kspec); 545 if (GNUNET_OK != res) 546 { 547 GNUNET_break_op (0); 548 mc->phase = (GNUNET_NO == res) 549 ? MELT_PHASE_RETURN_YES 550 : MELT_PHASE_RETURN_NO; 551 return; 552 } 553 /* Check for duplicate planchets. Technically a bug on 554 * the client side that is harmless for us, but still 555 * not allowed per protocol */ 556 for (size_t k = 0; k <= kappa; k++) 557 { 558 size_t max = (k == kappa) 559 ? idx 560 : mc->request.refresh.num_coins; 561 for (size_t i = 0; i < max; i++) 562 { 563 if (0 == 564 TALER_blinded_planchet_cmp ( 565 &mc->request.planchets[kappa][idx], 566 &mc->request.planchets[k][i])) 567 { 568 GNUNET_break_op (0); 569 GNUNET_JSON_parse_free (kspec); 570 SET_ERROR (mc, 571 MELT_ERROR_IDEMPOTENT_PLANCHET); 572 return; 573 } 574 } 575 } 576 } 577 /* Save the hash of the batch of planchets for index kappa */ 578 TALER_wallet_blinded_planchets_hash ( 579 mc->request.refresh.num_coins, 580 mc->request.planchets[kappa], 581 mc->request.denoms_h, 582 &mc->request.kappa_planchets_h.tuple[kappa]); 583 GNUNET_CRYPTO_hash_context_read ( 584 ctx, 585 &mc->request.kappa_planchets_h.tuple[kappa], 586 sizeof(mc->request.kappa_planchets_h.tuple[kappa])); 587 } 588 589 /* Finally calculate the total hash over all planchets */ 590 GNUNET_CRYPTO_hash_context_finish ( 591 ctx, 592 &mc->request.refresh.planchets_h.hash); 593 } 594 mc->ksh = TEH_keys_get_state (); 595 if (NULL == mc->ksh) 596 { 597 GNUNET_break (0); 598 SET_ERROR (mc, 599 MELT_ERROR_KEYS_MISSING); 600 return; 601 } 602 mc->phase = MELT_PHASE_CHECK_MELT_VALID; 603 } 604 605 606 /** 607 * Check if the given denomination is still or already valid, has not been 608 * revoked and potentically supports age restriction. 609 * 610 * @param[in,out] mc context for the melt operation 611 * @param denom_h Hash of the denomination key to check 612 * @param[out] pdk denomination key found, might be NULL 613 * @return #GNUNET_OK when denomation was found and valid, 614 * #GNUNET_NO when denomination is not valid at this time 615 * #GNUNET_SYSERR otherwise (denomination invalid), with finish_loop called. 616 */ 617 static enum GNUNET_GenericReturnValue 618 find_denomination ( 619 struct MeltContext *mc, 620 const struct TALER_DenominationHashP *denom_h, 621 struct TEH_DenominationKey **pdk) 622 { 623 struct TEH_DenominationKey *dk; 624 625 *pdk = NULL; 626 GNUNET_assert (NULL != mc->ksh); 627 dk = TEH_keys_denomination_by_hash_from_state ( 628 mc->ksh, 629 denom_h, 630 NULL, 631 NULL); 632 if (NULL == dk) 633 { 634 SET_ERROR_WITH_DETAIL (mc, 635 MELT_ERROR_DENOMINATION_KEY_UNKNOWN, 636 denom_h, 637 *denom_h); 638 return GNUNET_SYSERR; 639 } 640 *pdk = dk; 641 642 if (GNUNET_TIME_absolute_is_past ( 643 dk->meta.expire_withdraw.abs_time)) 644 { 645 SET_ERROR_WITH_DETAIL (mc, 646 MELT_ERROR_DENOMINATION_EXPIRED, 647 denom_h, 648 *denom_h); 649 /** 650 * Note that we return GNUNET_NO here. 651 * This way phase_check_melt_valid can react 652 * to it as a non-error case and do the zombie check. 653 */ 654 return GNUNET_NO; 655 } 656 657 if (GNUNET_TIME_absolute_is_future ( 658 dk->meta.start.abs_time)) 659 { 660 GNUNET_break_op (0); 661 SET_ERROR_WITH_DETAIL (mc, 662 MELT_ERROR_DENOMINATION_VALIDITY_IN_FUTURE, 663 denom_h, 664 *denom_h); 665 return GNUNET_SYSERR; 666 } 667 668 if (dk->recoup_possible) 669 { 670 SET_ERROR (mc, 671 MELT_ERROR_DENOMINATION_REVOKED); 672 return GNUNET_SYSERR; 673 } 674 675 /* In case of age melt, make sure that the denomitation supports age restriction */ 676 if (! (mc->request.refresh.coin.no_age_commitment) && 677 (0 == dk->denom_pub.age_mask.bits)) 678 { 679 GNUNET_break_op (0); 680 SET_ERROR_WITH_DETAIL (mc, 681 MELT_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION, 682 denom_h, 683 *denom_h); 684 return GNUNET_SYSERR; 685 } 686 if ((mc->request.refresh.coin.no_age_commitment) && 687 (0 != dk->denom_pub.age_mask.bits)) 688 { 689 GNUNET_break_op (0); 690 SET_ERROR (mc, 691 MELT_ERROR_AGE_RESTRICTION_COMMITMENT_INVALID); 692 return GNUNET_SYSERR; 693 } 694 695 return GNUNET_OK; 696 } 697 698 699 /** 700 * Check if the given array of hashes of denomination_keys 701 * - belong to valid denominations 702 * - calculate the total amount of the denominations including fees 703 * for melt. 704 * 705 * @param mc context of the melt to check keys for 706 */ 707 static void 708 phase_check_keys ( 709 struct MeltContext *mc) 710 { 711 bool is_cs_denom[mc->request.refresh.num_coins]; 712 713 memset (is_cs_denom, 714 0, 715 sizeof(is_cs_denom)); 716 717 mc->request.refresh.denom_serials = 718 GNUNET_new_array (mc->request.refresh.num_coins, 719 uint64_t); 720 GNUNET_assert (GNUNET_OK == 721 TALER_amount_set_zero (TEH_currency, 722 &mc->request.amount)); 723 724 /* Calculate the total value and withdraw fees for the fresh coins */ 725 for (size_t i = 0; i < mc->request.refresh.num_coins; i++) 726 { 727 struct TEH_DenominationKey *dk; 728 729 if (GNUNET_OK != 730 find_denomination ( 731 mc, 732 &mc->request.denoms_h[i], 733 &dk)) 734 return; 735 736 if (GNUNET_CRYPTO_BSA_CS == 737 dk->denom_pub.bsign_pub_key->cipher) 738 { 739 if (mc->request.refresh.no_blinding_seed) 740 { 741 GNUNET_break_op (0); 742 SET_ERROR (mc, 743 MELT_ERROR_BLINDING_SEED_REQUIRED); 744 return; 745 } 746 mc->request.refresh.num_cs_r_values++; 747 is_cs_denom[i] = true; 748 } 749 /* Ensure the ciphers from the planchets match the denominations'. */ 750 { 751 for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) 752 { 753 if (dk->denom_pub.bsign_pub_key->cipher != 754 mc->request.planchets[k][i].blinded_message->cipher) 755 { 756 GNUNET_break_op (0); 757 SET_ERROR (mc, 758 MELT_ERROR_COIN_CIPHER_MISMATCH); 759 return; 760 } 761 } 762 } 763 /* Accumulate the values */ 764 if (0 > TALER_amount_add (&mc->request.amount, 765 &mc->request.amount, 766 &dk->meta.value)) 767 { 768 GNUNET_break_op (0); 769 SET_ERROR (mc, 770 MELT_ERROR_AMOUNT_OVERFLOW); 771 return; 772 } 773 /* Accumulate the withdraw fees for the fresh coins */ 774 if (0 > TALER_amount_add (&mc->request.amount, 775 &mc->request.amount, 776 &dk->meta.fees.withdraw)) 777 { 778 GNUNET_break_op (0); 779 SET_ERROR (mc, 780 MELT_ERROR_AMOUNT_PLUS_FEE_OVERFLOW); 781 return; 782 } 783 mc->request.refresh.denom_serials[i] = dk->meta.serial; 784 } 785 786 /** 787 * Calculate the amount (with withdraw fee) plus refresh fee and 788 * compare with the value provided by the client in the request. 789 */ 790 { 791 struct TALER_Amount amount_with_fee; 792 793 if (0 > TALER_amount_add (&amount_with_fee, 794 &mc->request.amount, 795 &mc->melted_coin_denom->meta.fees.refresh)) 796 { 797 GNUNET_break_op (0); 798 SET_ERROR (mc, 799 MELT_ERROR_AMOUNT_PLUS_FEE_OVERFLOW); 800 return; 801 } 802 803 if (0 != TALER_amount_cmp (&amount_with_fee, 804 &mc->request.refresh.amount_with_fee)) 805 { 806 GNUNET_break_op (0); 807 SET_ERROR (mc, 808 MELT_ERROR_AMOUNT_WITH_FEE_INCORRECT); 809 return; 810 } 811 } 812 813 /* Save the indices of CS denominations */ 814 if (0 < mc->request.refresh.num_cs_r_values) 815 { 816 size_t j = 0; 817 818 mc->request.cs_indices = GNUNET_new_array ( 819 mc->request.refresh.num_cs_r_values, 820 uint32_t); 821 822 for (size_t i = 0; i < mc->request.refresh.num_coins; i++) 823 { 824 if (is_cs_denom[i]) 825 mc->request.cs_indices[j++] = i; 826 } 827 } 828 mc->phase++; 829 } 830 831 832 /** 833 * Check that the client signature authorizing the melt is valid. 834 * 835 * @param[in,out] mc request context to check 836 */ 837 static void 838 phase_check_coin_signature ( 839 struct MeltContext *mc) 840 { 841 /* We can now compute the commitment */ 842 { 843 struct TALER_KappaHashBlindedPlanchetsP k_bps_h = {0}; 844 845 for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) 846 TALER_wallet_blinded_planchets_hash ( 847 mc->request.refresh.num_coins, 848 mc->request.planchets[k], 849 mc->request.denoms_h, 850 &k_bps_h.tuple[k]); 851 852 TALER_refresh_get_commitment_v27 ( 853 &mc->request.refresh.rc, 854 &mc->request.refresh.refresh_seed, 855 mc->request.no_blinding_seed 856 ? NULL 857 : &mc->request.refresh.blinding_seed, 858 &k_bps_h, 859 &mc->request.refresh.coin.coin_pub, 860 &mc->request.refresh.amount_with_fee); 861 } 862 863 864 TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; 865 if (GNUNET_OK != 866 TALER_wallet_melt_verify ( 867 &mc->request.refresh.amount_with_fee, 868 &mc->melted_coin_denom->meta.fees.refresh, 869 &mc->request.refresh.rc, 870 &mc->request.refresh.coin.denom_pub_hash, 871 &mc->request.refresh.coin.h_age_commitment, 872 &mc->request.refresh.coin.coin_pub, 873 &mc->request.refresh.coin_sig)) 874 { 875 GNUNET_break_op (0); 876 SET_ERROR (mc, 877 MELT_ERROR_COIN_SIGNATURE_INVALID); 878 return; 879 } 880 881 mc->phase++; 882 } 883 884 885 /** 886 * Check for information about the melted coin's denomination, 887 * extracting its validity status and fee structure. 888 * Baseline: check if deposits/refreshes are generally 889 * simply still allowed for this denomination. 890 * 891 * @param mc parsed request information 892 */ 893 static void 894 phase_check_melt_valid (struct MeltContext *mc) 895 { 896 enum MeltPhase current_phase = mc->phase; 897 /** 898 * Find the old coin's denomination. 899 * Note that we return only on GNUNET_SYSERR, 900 * because GNUNET_NO for the expired denomination 901 * will be handled below, with the zombie-check. 902 */ 903 if (GNUNET_SYSERR == 904 find_denomination (mc, 905 &mc->request.refresh.coin.denom_pub_hash, 906 &mc->melted_coin_denom)) 907 return; 908 909 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 910 "Melted coin's denomination is worth %s\n", 911 TALER_amount2s (&mc->melted_coin_denom->meta.value)); 912 913 /* sanity-check that "total melt amount > melt fee" */ 914 if (0 < 915 TALER_amount_cmp (&mc->melted_coin_denom->meta.fees.refresh, 916 &mc->request.refresh.amount_with_fee)) 917 { 918 GNUNET_break_op (0); 919 SET_ERROR (mc, 920 MELT_ERROR_FEES_EXCEED_CONTRIBUTION); 921 return; 922 } 923 924 if (GNUNET_OK != 925 TALER_test_coin_valid (&mc->request.refresh.coin, 926 &mc->melted_coin_denom->denom_pub)) 927 { 928 GNUNET_break_op (0); 929 SET_ERROR (mc, 930 MELT_ERROR_DENOMINATION_SIGNATURE_INVALID); 931 return; 932 } 933 934 /** 935 * find_denomination might have set the phase to 936 * produce an error, but we are still investigating. 937 * We reset the phase. 938 */ 939 mc->phase = current_phase; 940 mc->error.code = MELT_ERROR_NONE; 941 942 if (GNUNET_TIME_absolute_is_past ( 943 mc->melted_coin_denom->meta.expire_deposit.abs_time)) 944 { 945 /** 946 * We are past deposit expiration time, but maybe this is a zombie? 947 */ 948 struct TALER_DenominationHashP denom_hash; 949 enum GNUNET_DB_QueryStatus qs; 950 951 /* Check that the coin is dirty (we have seen it before), as we will 952 not just allow melting of a *fresh* coin where the denomination was 953 revoked (those must be recouped) */ 954 qs = TEH_plugin->get_coin_denomination ( 955 TEH_plugin->cls, 956 &mc->request.refresh.coin.coin_pub, 957 &mc->known_coin_id, 958 &denom_hash); 959 if (0 > qs) 960 { 961 /* There is no good reason for a serialization failure here: */ 962 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); 963 SET_ERROR (mc, 964 MELT_ERROR_DB_FETCH_FAILED); 965 return; 966 } 967 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 968 { 969 /* We never saw this coin before, so _this_ justification is not OK. 970 * Note that the error was already set in find_denominations. */ 971 GNUNET_assert (MELT_ERROR_DENOMINATION_EXPIRED == 972 mc->error.code); 973 GNUNET_assert (MELT_PHASE_GENERATE_REPLY_ERROR == 974 mc->phase); 975 return; 976 } 977 /* sanity check */ 978 if (0 != 979 GNUNET_memcmp (&denom_hash, 980 &mc->request.refresh.coin.denom_pub_hash)) 981 { 982 GNUNET_break_op (0); 983 SET_ERROR_WITH_DETAIL (mc, 984 MELT_COIN_CONFLICTING_DENOMINATION_KEY, 985 denom_h, 986 denom_hash); 987 return; 988 } 989 /* Minor optimization: no need to run the 990 "ensure_coin_known" part of the transaction */ 991 mc->coin_is_dirty = true; 992 /* check later that zombie is satisfied */ 993 mc->zombie_required = true; 994 } 995 mc->phase++; 996 } 997 998 999 /** 1000 * The request for melt was parsed successfully. 1001 * Sign and persist the chosen blinded coins for the reveal step. 1002 * 1003 * @param mc The context for the current melt request 1004 */ 1005 static void 1006 phase_prepare_transaction ( 1007 struct MeltContext *mc) 1008 { 1009 mc->request.refresh.denom_sigs 1010 = GNUNET_new_array ( 1011 mc->request.refresh.num_coins, 1012 struct TALER_BlindedDenominationSignature); 1013 mc->request.refresh.noreveal_index = 1014 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, 1015 TALER_CNC_KAPPA); 1016 1017 /* Choose and sign the coins */ 1018 { 1019 struct TEH_CoinSignData csds[mc->request.refresh.num_coins]; 1020 enum TALER_ErrorCode ec_denomination_sign; 1021 size_t noreveal_idx = mc->request.refresh.noreveal_index; 1022 1023 memset (csds, 1024 0, 1025 sizeof(csds)); 1026 1027 /* Pick the chosen blinded coins */ 1028 for (size_t i = 0; i<mc->request.refresh.num_coins; i++) 1029 { 1030 csds[i].bp = &mc->request.planchets[noreveal_idx][i]; 1031 csds[i].h_denom_pub = &mc->request.denoms_h[i]; 1032 } 1033 1034 ec_denomination_sign = TEH_keys_denomination_batch_sign ( 1035 mc->request.refresh.num_coins, 1036 csds, 1037 true, /* for melt */ 1038 mc->request.refresh.denom_sigs); 1039 if (TALER_EC_NONE != ec_denomination_sign) 1040 { 1041 GNUNET_break (0); 1042 SET_ERROR_WITH_FIELD (mc, 1043 MELT_ERROR_DENOMINATION_SIGN, 1044 ec_denomination_sign); 1045 return; 1046 } 1047 1048 /* Save the hash of chosen planchets */ 1049 mc->request.refresh.selected_h = 1050 mc->request.kappa_planchets_h.tuple[noreveal_idx]; 1051 1052 /** 1053 * For the denominations with cipher CS, calculate the R-values 1054 * and save the choices we made now, as at a later point, the 1055 * private keys for the denominations might now be available anymore 1056 * to make the same choice again. 1057 */ 1058 if (0 < mc->request.refresh.num_cs_r_values) 1059 { 1060 size_t num_cs_r_values = mc->request.refresh.num_cs_r_values; 1061 struct TEH_CsDeriveData cdds[num_cs_r_values]; 1062 struct GNUNET_CRYPTO_CsSessionNonce nonces[num_cs_r_values]; 1063 1064 memset (nonces, 0, sizeof(nonces)); 1065 mc->request.refresh.cs_r_values 1066 = GNUNET_new_array ( 1067 num_cs_r_values, 1068 struct GNUNET_CRYPTO_CSPublicRPairP); 1069 mc->request.refresh.cs_r_choices = 0; 1070 1071 GNUNET_assert (! mc->request.refresh.no_blinding_seed); 1072 TALER_cs_derive_nonces_from_seed ( 1073 &mc->request.refresh.blinding_seed, 1074 true, /* for melt */ 1075 num_cs_r_values, 1076 mc->request.cs_indices, 1077 nonces); 1078 1079 for (size_t i = 0; i < num_cs_r_values; i++) 1080 { 1081 size_t idx = mc->request.cs_indices[i]; 1082 1083 GNUNET_assert (idx < mc->request.refresh.num_coins); 1084 cdds[i].h_denom_pub = &mc->request.denoms_h[idx]; 1085 cdds[i].nonce = &nonces[i]; 1086 } 1087 1088 /** 1089 * Let the crypto helper generate the R-values and 1090 * make the choices 1091 */ 1092 if (TALER_EC_NONE != 1093 TEH_keys_denomination_cs_batch_r_pub_simple ( 1094 mc->request.refresh.num_cs_r_values, 1095 cdds, 1096 true, /* for melt */ 1097 mc->request.refresh.cs_r_values)) 1098 { 1099 GNUNET_break (0); 1100 SET_ERROR (mc, 1101 MELT_ERROR_CRYPTO_HELPER); 1102 return; 1103 } 1104 1105 /* Now save the choices for the selected bits */ 1106 for (size_t i = 0; i < num_cs_r_values; i++) 1107 { 1108 size_t idx = mc->request.cs_indices[i]; 1109 1110 struct TALER_BlindedDenominationSignature *sig = 1111 &mc->request.refresh.denom_sigs[idx]; 1112 uint8_t bit = sig->blinded_sig->details.blinded_cs_answer.b; 1113 1114 mc->request.refresh.cs_r_choices |= bit << i; 1115 GNUNET_static_assert ( 1116 TALER_MAX_COINS <= 1117 sizeof(mc->request.refresh.cs_r_choices) * 8); 1118 } 1119 } 1120 } 1121 mc->phase++; 1122 } 1123 1124 1125 /** 1126 * Generates response for the melt request. 1127 * 1128 * @param mc melt operation context 1129 */ 1130 static void 1131 phase_generate_reply_success (struct MeltContext *mc) 1132 { 1133 struct TALER_EXCHANGEDB_Refresh_v27 *db_obj; 1134 struct TALER_ExchangePublicKeyP pub; 1135 struct TALER_ExchangeSignatureP sig; 1136 enum TALER_ErrorCode ec_confirmation_sign; 1137 1138 db_obj = mc->request.is_idempotent 1139 ? &mc->request.refresh_idem 1140 : &mc->request.refresh; 1141 ec_confirmation_sign = 1142 TALER_exchange_online_melt_confirmation_sign ( 1143 &TEH_keys_exchange_sign_, 1144 &db_obj->rc, 1145 db_obj->noreveal_index, 1146 &pub, 1147 &sig); 1148 if (TALER_EC_NONE != ec_confirmation_sign) 1149 { 1150 SET_ERROR_WITH_FIELD (mc, 1151 MELT_ERROR_CONFIRMATION_SIGN, 1152 ec_confirmation_sign); 1153 return; 1154 } 1155 1156 finish_loop (mc, 1157 TALER_MHD_REPLY_JSON_PACK ( 1158 mc->rc->connection, 1159 MHD_HTTP_OK, 1160 GNUNET_JSON_pack_uint64 ("noreveal_index", 1161 db_obj->noreveal_index), 1162 GNUNET_JSON_pack_data_auto ("exchange_sig", 1163 &sig), 1164 GNUNET_JSON_pack_data_auto ("exchange_pub", 1165 &pub))); 1166 } 1167 1168 1169 /** 1170 * Check if the melt request is replayed and we already have an answer. 1171 * If so, replay the existing answer and return the HTTP response. 1172 * 1173 * @param[in,out] mc parsed request data 1174 * @return true if the request is idempotent with an existing request 1175 * false if we did not find the request in the DB and did not set @a mret 1176 */ 1177 static bool 1178 melt_is_idempotent ( 1179 struct MeltContext *mc) 1180 { 1181 enum GNUNET_DB_QueryStatus qs; 1182 1183 qs = TEH_plugin->get_refresh ( 1184 TEH_plugin->cls, 1185 &mc->request.refresh.rc, 1186 &mc->request.refresh_idem); 1187 if (0 > qs) 1188 { 1189 /* FIXME: soft error not handled correctly! */ 1190 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1191 if (GNUNET_DB_STATUS_HARD_ERROR == qs) 1192 SET_ERROR_WITH_DETAIL (mc, 1193 MELT_ERROR_DB_FETCH_FAILED, 1194 db_fetch_context, 1195 "get_refresh"); 1196 return true; /* Well, kind-of. */ 1197 } 1198 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1199 return false; 1200 1201 mc->request.is_idempotent = true; 1202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1203 "request is idempotent\n"); 1204 1205 /* Generate idempotent reply */ 1206 TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_MELT]++; 1207 mc->phase = MELT_PHASE_GENERATE_REPLY_SUCCESS; 1208 mc->error.code = MELT_ERROR_NONE; 1209 return true; 1210 } 1211 1212 1213 /** 1214 * Reports an error, potentially with details. 1215 * That is, it puts a error-type specific response into the MHD queue. 1216 * It will do a idempotency check first, if needed for the error type. 1217 * 1218 * @param mc melt context 1219 */ 1220 static void 1221 phase_generate_reply_error ( 1222 struct MeltContext *mc) 1223 { 1224 GNUNET_assert (MELT_PHASE_GENERATE_REPLY_ERROR == mc->phase); 1225 GNUNET_assert (MELT_ERROR_NONE != mc->error.code); 1226 1227 if (IDEMPOTENCY_CHECK_REQUIRED (mc->error.code) && 1228 melt_is_idempotent (mc)) 1229 { 1230 return; 1231 } 1232 1233 switch (mc->error.code) 1234 { 1235 case MELT_ERROR_NONE: 1236 break; 1237 case MELT_ERROR_REQUEST_PARAMETER_MALFORMED: 1238 finish_loop (mc, 1239 TALER_MHD_reply_with_error ( 1240 mc->rc->connection, 1241 MHD_HTTP_BAD_REQUEST, 1242 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1243 mc->error.details.request_parameter_malformed)); 1244 return; 1245 case MELT_ERROR_KEYS_MISSING: 1246 finish_loop (mc, 1247 TALER_MHD_reply_with_error ( 1248 mc->rc->connection, 1249 MHD_HTTP_INTERNAL_SERVER_ERROR, 1250 TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, 1251 NULL)); 1252 return; 1253 case MELT_ERROR_DB_FETCH_FAILED: 1254 finish_loop (mc, 1255 TALER_MHD_reply_with_error ( 1256 mc->rc->connection, 1257 MHD_HTTP_INTERNAL_SERVER_ERROR, 1258 TALER_EC_GENERIC_DB_FETCH_FAILED, 1259 mc->error.details.db_fetch_context)); 1260 return; 1261 case MELT_ERROR_DB_INVARIANT_FAILURE: 1262 finish_loop (mc, 1263 TALER_MHD_reply_with_error ( 1264 mc->rc->connection, 1265 MHD_HTTP_INTERNAL_SERVER_ERROR, 1266 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 1267 NULL)); 1268 return; 1269 case MELT_ERROR_DB_PREFLIGHT_FAILURE: 1270 finish_loop (mc, 1271 TALER_MHD_reply_with_error ( 1272 mc->rc->connection, 1273 MHD_HTTP_INTERNAL_SERVER_ERROR, 1274 TALER_EC_GENERIC_DB_COMMIT_FAILED, 1275 "make_coin_known")); 1276 return; 1277 case MELT_ERROR_DB_MAKE_COIN_KNOW_FAILURE: 1278 finish_loop (mc, 1279 TALER_MHD_reply_with_error ( 1280 mc->rc->connection, 1281 MHD_HTTP_INTERNAL_SERVER_ERROR, 1282 TALER_EC_GENERIC_DB_START_FAILED, 1283 "preflight failure")); 1284 return; 1285 case MELT_ERROR_COIN_UNKNOWN: 1286 finish_loop (mc, 1287 TALER_MHD_reply_with_ec ( 1288 mc->rc->connection, 1289 TALER_EC_EXCHANGE_GENERIC_COIN_UNKNOWN, 1290 NULL)); 1291 return; 1292 case MELT_COIN_CONFLICTING_DENOMINATION_KEY: 1293 finish_loop (mc, 1294 TALER_MHD_reply_with_ec ( 1295 mc->rc->connection, 1296 TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY, 1297 TALER_B2S (&mc->error.details.denom_h))); 1298 return; 1299 case MELT_ERROR_COIN_EXPIRED_NO_ZOMBIE: 1300 finish_loop (mc, 1301 TALER_MHD_reply_with_error ( 1302 mc->rc->connection, 1303 MHD_HTTP_BAD_REQUEST, 1304 TALER_EC_EXCHANGE_MELT_COIN_EXPIRED_NO_ZOMBIE, 1305 NULL)); 1306 return; 1307 case MELT_ERROR_DENOMINATION_SIGN: 1308 finish_loop (mc, 1309 TALER_MHD_reply_with_ec ( 1310 mc->rc->connection, 1311 mc->error.details.ec_denomination_sign, 1312 NULL)); 1313 return; 1314 case MELT_ERROR_DENOMINATION_SIGNATURE_INVALID: 1315 finish_loop (mc, 1316 TALER_MHD_reply_with_error (mc->rc->connection, 1317 MHD_HTTP_FORBIDDEN, 1318 TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID, 1319 NULL)); 1320 return; 1321 case MELT_ERROR_DENOMINATION_KEY_UNKNOWN: 1322 GNUNET_break_op (0); 1323 finish_loop (mc, 1324 TEH_RESPONSE_reply_unknown_denom_pub_hash ( 1325 mc->rc->connection, 1326 &mc->error.details.denom_h)); 1327 return; 1328 case MELT_ERROR_DENOMINATION_EXPIRED: 1329 GNUNET_break_op (0); 1330 finish_loop (mc, 1331 TEH_RESPONSE_reply_expired_denom_pub_hash ( 1332 mc->rc->connection, 1333 &mc->error.details.denom_h, 1334 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, 1335 "MELT")); 1336 return; 1337 case MELT_ERROR_DENOMINATION_VALIDITY_IN_FUTURE: 1338 finish_loop (mc, 1339 TEH_RESPONSE_reply_expired_denom_pub_hash ( 1340 mc->rc->connection, 1341 &mc->error.details.denom_h, 1342 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, 1343 "MELT")); 1344 return; 1345 case MELT_ERROR_DENOMINATION_REVOKED: 1346 GNUNET_break_op (0); 1347 finish_loop (mc, 1348 TALER_MHD_reply_with_ec ( 1349 mc->rc->connection, 1350 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, 1351 NULL)); 1352 return; 1353 case MELT_ERROR_COIN_CIPHER_MISMATCH: 1354 finish_loop (mc, 1355 TALER_MHD_reply_with_ec ( 1356 mc->rc->connection, 1357 TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH, 1358 NULL)); 1359 return; 1360 case MELT_ERROR_BLINDING_SEED_REQUIRED: 1361 finish_loop (mc, 1362 TALER_MHD_reply_with_ec ( 1363 mc->rc->connection, 1364 TALER_EC_GENERIC_PARAMETER_MISSING, 1365 "blinding_seed")); 1366 return; 1367 case MELT_ERROR_CRYPTO_HELPER: 1368 finish_loop (mc, 1369 TALER_MHD_reply_with_ec ( 1370 mc->rc->connection, 1371 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 1372 NULL)); 1373 return; 1374 case MELT_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION: 1375 { 1376 char msg[256]; 1377 1378 GNUNET_snprintf (msg, 1379 sizeof(msg), 1380 "denomination %s does not support age restriction", 1381 GNUNET_h2s (&mc->error.details.denom_h.hash)); 1382 finish_loop (mc, 1383 TALER_MHD_reply_with_ec ( 1384 mc->rc->connection, 1385 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN, 1386 msg)); 1387 return; 1388 } 1389 case MELT_ERROR_AGE_RESTRICTION_COMMITMENT_INVALID: 1390 finish_loop (mc, 1391 TALER_MHD_reply_with_ec ( 1392 mc->rc->connection, 1393 TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID, 1394 "old_age_commitment_h")); 1395 return; 1396 case MELT_ERROR_AMOUNT_OVERFLOW: 1397 finish_loop (mc, 1398 TALER_MHD_reply_with_error ( 1399 mc->rc->connection, 1400 MHD_HTTP_BAD_REQUEST, 1401 TALER_EC_EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW, 1402 "amount")); 1403 return; 1404 case MELT_ERROR_AMOUNT_PLUS_FEE_OVERFLOW: 1405 finish_loop (mc, 1406 TALER_MHD_reply_with_error ( 1407 mc->rc->connection, 1408 MHD_HTTP_INTERNAL_SERVER_ERROR, 1409 TALER_EC_EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW, 1410 "amount+fee")); 1411 return; 1412 case MELT_ERROR_FEES_EXCEED_CONTRIBUTION: 1413 finish_loop (mc, 1414 TALER_MHD_reply_with_error (mc->rc->connection, 1415 MHD_HTTP_BAD_REQUEST, 1416 TALER_EC_EXCHANGE_MELT_FEES_EXCEED_CONTRIBUTION, 1417 NULL)); 1418 return; 1419 case MELT_ERROR_AMOUNT_WITH_FEE_INCORRECT: 1420 finish_loop (mc, 1421 TALER_MHD_reply_with_error ( 1422 mc->rc->connection, 1423 MHD_HTTP_BAD_REQUEST, 1424 TALER_EC_EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW, 1425 "value_with_fee incorrect")); 1426 return; 1427 case MELT_ERROR_CONFIRMATION_SIGN: 1428 finish_loop (mc, 1429 TALER_MHD_reply_with_ec ( 1430 mc->rc->connection, 1431 mc->error.details.ec_confirmation_sign, 1432 NULL)); 1433 return; 1434 case MELT_ERROR_INSUFFICIENT_FUNDS: 1435 finish_loop (mc, 1436 TEH_RESPONSE_reply_coin_insufficient_funds ( 1437 mc->rc->connection, 1438 TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, 1439 &mc->request.refresh.coin.denom_pub_hash, 1440 &mc->request.refresh.coin.coin_pub)); 1441 return; 1442 case MELT_ERROR_IDEMPOTENT_PLANCHET: 1443 finish_loop (mc, 1444 TALER_MHD_reply_with_error ( 1445 mc->rc->connection, 1446 MHD_HTTP_BAD_REQUEST, 1447 TALER_EC_GENERIC_PARAMETER_MALFORMED, /* FIXME: new error! */ 1448 "idempotent planchet")); 1449 return; 1450 case MELT_ERROR_NONCE_RESUSE: 1451 finish_loop (mc, 1452 TALER_MHD_reply_with_error ( 1453 mc->rc->connection, 1454 MHD_HTTP_BAD_REQUEST, 1455 TALER_EC_GENERIC_PARAMETER_MALFORMED, /* FIXME: new error */ 1456 "nonce reuse")); 1457 return; 1458 case MELT_ERROR_COIN_SIGNATURE_INVALID: 1459 finish_loop (mc, 1460 TALER_MHD_reply_with_ec ( 1461 mc->rc->connection, 1462 TALER_EC_EXCHANGE_MELT_COIN_SIGNATURE_INVALID, 1463 NULL)); 1464 return; 1465 } 1466 GNUNET_break (0); 1467 finish_loop (mc, 1468 TALER_MHD_reply_with_error ( 1469 mc->rc->connection, 1470 MHD_HTTP_INTERNAL_SERVER_ERROR, 1471 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 1472 "error phase without error")); 1473 } 1474 1475 1476 /** 1477 * Function implementing melt transaction. Runs the 1478 * transaction logic; IF it returns a non-error code, the transaction 1479 * logic MUST NOT queue a MHD response. IF it returns an hard error, 1480 * the transaction logic MUST queue a MHD response and set @a mhd_ret. 1481 * IF it returns the soft error code, the function MAY be called again 1482 * to retry and MUST not queue a MHD response. 1483 * 1484 * @param cls a `struct MeltContext *` 1485 * @param connection MHD request which triggered the transaction 1486 * @param[out] mhd_ret set to MHD response status for @a connection, 1487 * if transaction failed (!) 1488 * @return transaction status 1489 */ 1490 static enum GNUNET_DB_QueryStatus 1491 melt_transaction ( 1492 void *cls, 1493 struct MHD_Connection *connection, 1494 MHD_RESULT *mhd_ret) 1495 { 1496 struct MeltContext *mc = cls; 1497 enum GNUNET_DB_QueryStatus qs; 1498 bool balance_ok; 1499 bool found; 1500 bool nonce_reuse; 1501 uint32_t noreveal_index; 1502 struct TALER_Amount insufficient_funds; 1503 1504 qs = TEH_plugin->do_refresh (TEH_plugin->cls, 1505 &mc->request.refresh, 1506 &mc->now, 1507 &found, 1508 &noreveal_index, 1509 &mc->zombie_required, 1510 &nonce_reuse, 1511 &balance_ok, 1512 &insufficient_funds); 1513 if (0 > qs) 1514 { 1515 if (GNUNET_DB_STATUS_HARD_ERROR == qs) 1516 SET_ERROR_WITH_DETAIL (mc, 1517 MELT_ERROR_DB_FETCH_FAILED, 1518 db_fetch_context, 1519 "do_refresh"); 1520 return qs; 1521 } 1522 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1523 { 1524 GNUNET_break_op (0); 1525 SET_ERROR (mc, 1526 MELT_ERROR_COIN_UNKNOWN); 1527 return GNUNET_DB_STATUS_HARD_ERROR; 1528 } 1529 if (found) 1530 { 1531 /** 1532 * This request is idempotent, set the nonreveal_index 1533 * to the previous one and reply success. 1534 */ 1535 mc->request.refresh.noreveal_index = noreveal_index; 1536 mc->phase = MELT_PHASE_GENERATE_REPLY_SUCCESS; 1537 mc->error.code = MELT_ERROR_NONE; 1538 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 1539 } 1540 if (nonce_reuse) 1541 { 1542 GNUNET_break_op (0); 1543 SET_ERROR (mc, 1544 MELT_ERROR_NONCE_RESUSE); 1545 return GNUNET_DB_STATUS_HARD_ERROR; 1546 } 1547 if (! balance_ok) 1548 { 1549 GNUNET_break_op (0); 1550 SET_ERROR_WITH_FIELD (mc, 1551 MELT_ERROR_INSUFFICIENT_FUNDS, 1552 insufficient_funds); 1553 return GNUNET_DB_STATUS_HARD_ERROR; 1554 } 1555 if (mc->zombie_required) 1556 { 1557 GNUNET_break_op (0); 1558 SET_ERROR (mc, 1559 MELT_ERROR_COIN_EXPIRED_NO_ZOMBIE); 1560 return GNUNET_DB_STATUS_HARD_ERROR; 1561 } 1562 1563 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) 1564 TEH_METRICS_num_success[TEH_MT_SUCCESS_MELT]++; 1565 return qs; 1566 } 1567 1568 1569 /** 1570 * The request was prepared successfully. 1571 * Run the main DB transaction. 1572 * 1573 * @param mc The context for the current melt request 1574 */ 1575 static void 1576 phase_run_transaction ( 1577 struct MeltContext *mc) 1578 { 1579 if (GNUNET_SYSERR == 1580 TEH_plugin->preflight (TEH_plugin->cls)) 1581 { 1582 GNUNET_break (0); 1583 SET_ERROR (mc, 1584 MELT_ERROR_DB_PREFLIGHT_FAILURE); 1585 return; 1586 } 1587 1588 /* first, make sure coin is known */ 1589 if (! mc->coin_is_dirty) 1590 { 1591 MHD_RESULT mhd_ret = -1; 1592 enum GNUNET_DB_QueryStatus qs; 1593 1594 for (unsigned int tries = 0; tries<MAX_TRANSACTION_COMMIT_RETRIES; tries++) 1595 { 1596 qs = TEH_make_coin_known (&mc->request.refresh.coin, 1597 mc->rc->connection, 1598 &mc->known_coin_id, 1599 &mhd_ret); 1600 if (GNUNET_DB_STATUS_SOFT_ERROR != qs) 1601 break; 1602 } 1603 if (0 > qs) 1604 { 1605 GNUNET_break (0); 1606 /* Check if an answer has been queued */ 1607 switch (mhd_ret) 1608 { 1609 case MHD_NO: 1610 mc->phase = MELT_PHASE_RETURN_NO; 1611 return; 1612 case MHD_YES: 1613 mc->phase = MELT_PHASE_RETURN_YES; 1614 return; 1615 default: 1616 /* ignore */ 1617 } 1618 SET_ERROR (mc, 1619 MELT_ERROR_DB_MAKE_COIN_KNOW_FAILURE); 1620 return; 1621 } 1622 } 1623 1624 /* run main database transaction */ 1625 { 1626 MHD_RESULT mhd_ret = -1; 1627 enum GNUNET_GenericReturnValue ret; 1628 enum MeltPhase current_phase = mc->phase; 1629 1630 GNUNET_assert (MELT_PHASE_RUN_TRANSACTION == 1631 current_phase); 1632 ret = TEH_DB_run_transaction (mc->rc->connection, 1633 "run melt", 1634 TEH_MT_REQUEST_MELT, 1635 &mhd_ret, 1636 &melt_transaction, 1637 mc); 1638 if (GNUNET_OK != ret) 1639 { 1640 GNUNET_break (0); 1641 /* Check if an answer has been queued */ 1642 switch (mhd_ret) 1643 { 1644 case MHD_NO: 1645 mc->phase = MELT_PHASE_RETURN_NO; 1646 return; 1647 case MHD_YES: 1648 mc->phase = MELT_PHASE_RETURN_YES; 1649 return; 1650 default: 1651 /* ignore */ 1652 } 1653 GNUNET_assert (MELT_ERROR_NONE != mc->error.code); 1654 GNUNET_assert (MELT_PHASE_GENERATE_REPLY_ERROR == mc->phase); 1655 return; 1656 } 1657 /** 1658 * In case of idempotency (which is not an error condition), 1659 * the phase has changed in melt_transaction. 1660 * We simple return. 1661 */ 1662 if (current_phase != mc->phase) 1663 return; 1664 } 1665 mc->phase++; 1666 } 1667 1668 1669 MHD_RESULT 1670 TEH_handler_melt_v27 ( 1671 struct TEH_RequestContext *rc, 1672 const json_t *root, 1673 const char *const args[0]) 1674 { 1675 struct MeltContext *mc = rc->rh_ctx; 1676 1677 (void) args; 1678 if (NULL == mc) 1679 { 1680 mc = GNUNET_new (struct MeltContext); 1681 rc->rh_ctx = mc; 1682 rc->rh_cleaner = &clean_melt_rc; 1683 mc->rc = rc; 1684 mc->now = GNUNET_TIME_timestamp_get (); 1685 } 1686 1687 while (true) 1688 { 1689 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1690 "melt processing in phase %d\n", 1691 mc->phase); 1692 switch (mc->phase) 1693 { 1694 case MELT_PHASE_PARSE: 1695 phase_parse_request (mc, 1696 root); 1697 break; 1698 case MELT_PHASE_CHECK_MELT_VALID: 1699 phase_check_melt_valid (mc); 1700 break; 1701 case MELT_PHASE_CHECK_KEYS: 1702 phase_check_keys (mc); 1703 break; 1704 case MELT_PHASE_CHECK_COIN_SIGNATURE: 1705 phase_check_coin_signature (mc); 1706 break; 1707 case MELT_PHASE_PREPARE_TRANSACTION: 1708 phase_prepare_transaction (mc); 1709 break; 1710 case MELT_PHASE_RUN_TRANSACTION: 1711 phase_run_transaction (mc); 1712 break; 1713 case MELT_PHASE_GENERATE_REPLY_SUCCESS: 1714 phase_generate_reply_success (mc); 1715 break; 1716 case MELT_PHASE_GENERATE_REPLY_ERROR: 1717 phase_generate_reply_error (mc); 1718 break; 1719 case MELT_PHASE_RETURN_YES: 1720 return MHD_YES; 1721 case MELT_PHASE_RETURN_NO: 1722 return MHD_NO; 1723 } 1724 } 1725 }