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