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