taler-exchange-httpd_post-recoup-refresh.c (29136B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2017-2026 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-recoup-refresh.c 18 * @brief Handle /recoup-refresh requests: coins of revoked denominations 19 * that originated from one refresh operation are paid back to the 20 * old coin that was melted. Structured like the /withdraw handler 21 * as a phase state machine. 22 * @author Christian Grothoff 23 * @author Özgür Kesim 24 */ 25 #include <gnunet/gnunet_util_lib.h> 26 #include <gnunet/gnunet_json_lib.h> 27 #include <jansson.h> 28 #include <microhttpd.h> 29 #include "taler/taler_json_lib.h" 30 #include "taler/taler_mhd_lib.h" 31 #include "taler-exchange-httpd.h" 32 #include "taler-exchange-httpd_db.h" 33 #include "taler-exchange-httpd_common_recoup.h" 34 #include "taler-exchange-httpd_post-recoup-refresh.h" 35 #include "taler-exchange-httpd_responses.h" 36 #include "taler-exchange-httpd_get-keys.h" 37 #include "taler-exchange-httpd_get-metrics.h" 38 #include "exchangedb_lib.h" 39 #include "exchange-database/do_recoup_refresh.h" 40 #include "exchange-database/get_refresh.h" 41 #include "exchange-database/rollback.h" 42 43 44 /** 45 * The different types of errors that might occur, sorted by name. 46 */ 47 enum RecoupRefreshError 48 { 49 RECOUP_REFRESH_ERROR_NONE, 50 RECOUP_REFRESH_ERROR_BATCH_SIZE_MISMATCH, 51 RECOUP_REFRESH_ERROR_BLINDING_FAILED, 52 RECOUP_REFRESH_ERROR_COIN_SIGNATURE_INVALID, 53 RECOUP_REFRESH_ERROR_COMMITMENT_MISMATCH, 54 RECOUP_REFRESH_ERROR_CONFIRMATION_SIGN, 55 RECOUP_REFRESH_ERROR_DB_FETCH_FAILED, 56 RECOUP_REFRESH_ERROR_DB_INVARIANT_FAILURE, 57 RECOUP_REFRESH_ERROR_DENOMINATION_EXPIRED, 58 RECOUP_REFRESH_ERROR_DENOMINATION_KEY_UNKNOWN, 59 RECOUP_REFRESH_ERROR_DENOMINATION_MISMATCH, 60 RECOUP_REFRESH_ERROR_DENOMINATION_NOT_ELIGIBLE, 61 RECOUP_REFRESH_ERROR_DENOMINATION_SIGNATURE_INVALID, 62 RECOUP_REFRESH_ERROR_DENOMINATION_VALIDITY_IN_FUTURE, 63 RECOUP_REFRESH_ERROR_INSUFFICIENT_FUNDS, 64 RECOUP_REFRESH_ERROR_KEYS_MISSING, 65 RECOUP_REFRESH_ERROR_REQUEST_PARAMETER_MALFORMED, 66 RECOUP_REFRESH_ERROR_MELT_NOT_FOUND, 67 }; 68 69 70 /** 71 * Context for a /recoup-refresh request. 72 */ 73 struct RecoupRefreshContext 74 { 75 76 /** 77 * Processing phase we are in. The ordering matters, as we 78 * progress through them by incrementing the phase in the happy path. 79 */ 80 enum 81 { 82 RECOUP_REFRESH_PHASE_PARSE = 0, 83 RECOUP_REFRESH_PHASE_LOOKUP_OPERATION, 84 RECOUP_REFRESH_PHASE_CHECK_KEYS, 85 RECOUP_REFRESH_PHASE_VERIFY_COINS, 86 RECOUP_REFRESH_PHASE_RUN_TRANSACTION, 87 RECOUP_REFRESH_PHASE_GENERATE_REPLY_SUCCESS, 88 RECOUP_REFRESH_PHASE_GENERATE_REPLY_ERROR, 89 RECOUP_REFRESH_PHASE_RETURN_NO, 90 RECOUP_REFRESH_PHASE_RETURN_YES, 91 } phase; 92 93 /** 94 * Request context. 95 */ 96 const struct TEH_RequestContext *rc; 97 98 /** 99 * Current time for the DB transaction. 100 */ 101 struct GNUNET_TIME_Timestamp now; 102 103 /** 104 * Captures all parameters provided in the JSON request. 105 */ 106 struct 107 { 108 /** 109 * Old coin the coins were refreshed from and that is credited. 110 */ 111 struct TALER_CoinSpendPublicKeyP old_coin_pub; 112 113 /** 114 * Commitment of the refresh operation. 115 */ 116 struct TALER_RefreshCommitmentP rc; 117 118 /** 119 * Number of entries in @e coins. 120 */ 121 size_t num_coins; 122 123 /** 124 * The coins of the signed batch, in order. 125 */ 126 struct TEH_RecoupCoin *coins; 127 } request; 128 129 /** 130 * The refresh operation, as recorded in the database. 131 */ 132 struct TALER_EXCHANGEDB_Refresh_vDOLDPLUS refresh; 133 134 /** 135 * True once @e refresh holds data that must be released. 136 */ 137 bool have_refresh; 138 139 /** 140 * Errors occurring during evaluation of the request. In phase 141 * #RECOUP_REFRESH_PHASE_GENERATE_REPLY_ERROR an appropriate error 142 * message is prepared and sent to the client. 143 */ 144 struct 145 { 146 /** 147 * The (internal) error code. 148 */ 149 enum RecoupRefreshError code; 150 151 /** 152 * Details for some of the errors. 153 */ 154 union 155 { 156 const char *request_parameter_malformed; 157 const char *db_fetch_context; 158 /** 159 * For all errors related to a particular denomination. 160 */ 161 const struct TALER_DenominationHashP *denom_h; 162 enum TALER_ErrorCode ec_confirmation_sign; 163 /** 164 * The coin that has no residual value left. 165 */ 166 const struct TEH_RecoupCoin *insufficient_funds; 167 } details; 168 } error; 169 }; 170 171 172 /** 173 * The following macros set the given error code, 174 * set the phase to RECOUP_REFRESH_PHASE_GENERATE_REPLY_ERROR, 175 * and optionally set the given field to the given value. 176 */ 177 #define SET_ERROR(wc, ec) \ 178 do \ 179 { GNUNET_static_assert (RECOUP_REFRESH_ERROR_NONE != ec); \ 180 (wc)->error.code = (ec); \ 181 (wc)->phase = RECOUP_REFRESH_PHASE_GENERATE_REPLY_ERROR; \ 182 } while (0) 183 #define SET_ERROR_WITH_DETAIL(wc, ec, field, value) \ 184 do \ 185 { GNUNET_static_assert (RECOUP_REFRESH_ERROR_NONE != ec); \ 186 (wc)->error.code = (ec); \ 187 (wc)->error.details.field = (value); \ 188 (wc)->phase = RECOUP_REFRESH_PHASE_GENERATE_REPLY_ERROR; \ 189 } while (0) 190 191 192 /** 193 * Terminate the phase loop with the given MHD result. 194 * 195 * @param[in,out] wc context to finish 196 * @param mres result to return from the handler 197 */ 198 static void 199 finish_loop (struct RecoupRefreshContext *wc, 200 enum MHD_Result mres) 201 { 202 wc->phase = (MHD_YES == mres) 203 ? RECOUP_REFRESH_PHASE_RETURN_YES 204 : RECOUP_REFRESH_PHASE_RETURN_NO; 205 } 206 207 208 /** 209 * Translate an error of the shared recoup logic into our error state. 210 * 211 * @param[in,out] wc context to set the error on 212 * @param err error reported by the shared logic 213 * @param details details reported by the shared logic 214 */ 215 static void 216 set_common_error (struct RecoupRefreshContext *wc, 217 enum TEH_RecoupError err, 218 const union TEH_RecoupErrorDetails *details) 219 { 220 switch (err) 221 { 222 case TEH_RECOUP_ERROR_NONE: 223 GNUNET_assert (0); 224 return; 225 case TEH_RECOUP_ERROR_REQUEST_PARAMETER_MALFORMED: 226 SET_ERROR_WITH_DETAIL (wc, 227 RECOUP_REFRESH_ERROR_REQUEST_PARAMETER_MALFORMED, 228 request_parameter_malformed, 229 details->hint); 230 return; 231 case TEH_RECOUP_ERROR_KEYS_MISSING: 232 SET_ERROR (wc, 233 RECOUP_REFRESH_ERROR_KEYS_MISSING); 234 return; 235 case TEH_RECOUP_ERROR_DENOMINATION_KEY_UNKNOWN: 236 SET_ERROR_WITH_DETAIL (wc, 237 RECOUP_REFRESH_ERROR_DENOMINATION_KEY_UNKNOWN, 238 denom_h, 239 details->denom_h); 240 return; 241 case TEH_RECOUP_ERROR_DENOMINATION_MISMATCH: 242 SET_ERROR_WITH_DETAIL (wc, 243 RECOUP_REFRESH_ERROR_DENOMINATION_MISMATCH, 244 denom_h, 245 details->denom_h); 246 return; 247 case TEH_RECOUP_ERROR_DENOMINATION_NOT_ELIGIBLE: 248 SET_ERROR_WITH_DETAIL (wc, 249 RECOUP_REFRESH_ERROR_DENOMINATION_NOT_ELIGIBLE, 250 denom_h, 251 details->denom_h); 252 return; 253 case TEH_RECOUP_ERROR_DENOMINATION_EXPIRED: 254 SET_ERROR_WITH_DETAIL (wc, 255 RECOUP_REFRESH_ERROR_DENOMINATION_EXPIRED, 256 denom_h, 257 details->denom_h); 258 return; 259 case TEH_RECOUP_ERROR_DENOMINATION_VALIDITY_IN_FUTURE: 260 SET_ERROR_WITH_DETAIL (wc, 261 RECOUP_REFRESH_ERROR_DENOMINATION_VALIDITY_IN_FUTURE, 262 denom_h, 263 details->denom_h); 264 return; 265 case TEH_RECOUP_ERROR_DB_INVARIANT_FAILURE: 266 SET_ERROR (wc, 267 RECOUP_REFRESH_ERROR_DB_INVARIANT_FAILURE); 268 return; 269 case TEH_RECOUP_ERROR_DENOMINATION_SIGNATURE_INVALID: 270 SET_ERROR (wc, 271 RECOUP_REFRESH_ERROR_DENOMINATION_SIGNATURE_INVALID); 272 return; 273 case TEH_RECOUP_ERROR_COIN_SIGNATURE_INVALID: 274 SET_ERROR (wc, 275 RECOUP_REFRESH_ERROR_COIN_SIGNATURE_INVALID); 276 return; 277 case TEH_RECOUP_ERROR_BLINDING_FAILED: 278 SET_ERROR (wc, 279 RECOUP_REFRESH_ERROR_BLINDING_FAILED); 280 return; 281 case TEH_RECOUP_ERROR_COMMITMENT_MISMATCH: 282 SET_ERROR (wc, 283 RECOUP_REFRESH_ERROR_COMMITMENT_MISMATCH); 284 return; 285 } 286 GNUNET_assert (0); 287 } 288 289 290 /** 291 * Parse the request. 292 * 293 * @param[in,out] wc context of the request 294 * @param root the JSON body 295 */ 296 static void 297 phase_parse (struct RecoupRefreshContext *wc, 298 const json_t *root) 299 { 300 const json_t *j_coin_data; 301 struct GNUNET_JSON_Specification spec[] = { 302 GNUNET_JSON_spec_fixed_auto ("old_coin_pub", 303 &wc->request.old_coin_pub), 304 GNUNET_JSON_spec_fixed_auto ("rc", 305 &wc->request.rc), 306 GNUNET_JSON_spec_array_const ("coin_data", 307 &j_coin_data), 308 GNUNET_JSON_spec_end () 309 }; 310 enum GNUNET_GenericReturnValue res; 311 const char *hint; 312 enum MHD_Result mret; 313 314 res = TALER_MHD_parse_json_data (wc->rc->connection, 315 root, 316 spec); 317 if (GNUNET_OK != res) 318 { 319 GNUNET_break_op (0); 320 finish_loop (wc, 321 (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES); 322 return; 323 } 324 res = TEH_recoup_parse_coin_data (wc->rc->connection, 325 j_coin_data, 326 &wc->request.num_coins, 327 &wc->request.coins, 328 &hint, 329 &mret); 330 switch (res) 331 { 332 case GNUNET_OK: 333 wc->phase++; 334 return; 335 case GNUNET_NO: 336 finish_loop (wc, 337 mret); 338 return; 339 case GNUNET_SYSERR: 340 SET_ERROR_WITH_DETAIL (wc, 341 RECOUP_REFRESH_ERROR_REQUEST_PARAMETER_MALFORMED, 342 request_parameter_malformed, 343 hint); 344 return; 345 } 346 GNUNET_assert (0); 347 } 348 349 350 /** 351 * Find the refresh operation the coins originated from. 352 * 353 * @param[in,out] wc context of the request 354 */ 355 static void 356 phase_lookup_operation (struct RecoupRefreshContext *wc) 357 { 358 enum GNUNET_DB_QueryStatus qs; 359 uint8_t max_retries = 3; 360 361 while (0 < max_retries--) 362 { 363 qs = TALER_EXCHANGEDB_get_refresh (TEH_pg, 364 &wc->request.rc, 365 &wc->refresh); 366 if (GNUNET_DB_STATUS_SOFT_ERROR != qs) 367 break; 368 } 369 if (0 > qs) 370 { 371 GNUNET_break (0); 372 SET_ERROR_WITH_DETAIL (wc, 373 RECOUP_REFRESH_ERROR_DB_FETCH_FAILED, 374 db_fetch_context, 375 "get_refresh"); 376 return; 377 } 378 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 379 { 380 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 381 "Recoup requested for unknown refresh commitment %s\n", 382 GNUNET_h2s (&wc->request.rc.session_hash)); 383 SET_ERROR (wc, 384 RECOUP_REFRESH_ERROR_MELT_NOT_FOUND); 385 return; 386 } 387 wc->have_refresh = true; 388 if (0 != 389 GNUNET_memcmp (&wc->refresh.coin.coin_pub, 390 &wc->request.old_coin_pub)) 391 { 392 GNUNET_break_op (0); 393 SET_ERROR (wc, 394 RECOUP_REFRESH_ERROR_MELT_NOT_FOUND); 395 return; 396 } 397 if (wc->refresh.num_coins != wc->request.num_coins) 398 { 399 GNUNET_break_op (0); 400 SET_ERROR (wc, 401 RECOUP_REFRESH_ERROR_BATCH_SIZE_MISMATCH); 402 return; 403 } 404 wc->phase++; 405 } 406 407 408 /** 409 * Check the denominations of the batch. 410 * 411 * @param[in,out] wc context of the request 412 */ 413 static void 414 phase_check_keys (struct RecoupRefreshContext *wc) 415 { 416 union TEH_RecoupErrorDetails details; 417 enum TEH_RecoupError err; 418 419 err = TEH_recoup_check_keys (wc->request.num_coins, 420 wc->request.coins, 421 wc->refresh.denom_serials, 422 ! wc->refresh.no_blinding_seed, 423 &details); 424 if (TEH_RECOUP_ERROR_NONE != err) 425 { 426 set_common_error (wc, 427 err, 428 &details); 429 return; 430 } 431 wc->phase++; 432 } 433 434 435 /** 436 * Verify the disclosed coins and that the batch matches the 437 * commitment of the refresh operation. 438 * 439 * @param[in,out] wc context of the request 440 */ 441 static void 442 phase_verify_coins (struct RecoupRefreshContext *wc) 443 { 444 union TEH_RecoupErrorDetails details; 445 enum TEH_RecoupError err; 446 447 /* The exchange signed the batch at the noreveal_index, whose 448 hash was recorded as selected_h. */ 449 err = TEH_recoup_verify_coins (wc->request.num_coins, 450 wc->request.coins, 451 true, /* for melt */ 452 wc->refresh.no_blinding_seed 453 ? NULL 454 : &wc->refresh.blinding_seed, 455 wc->refresh.num_cs_r_values, 456 wc->refresh.cs_r_values, 457 &wc->refresh.selected_h, 458 &details); 459 if (TEH_RECOUP_ERROR_NONE != err) 460 { 461 set_common_error (wc, 462 err, 463 &details); 464 return; 465 } 466 wc->phase++; 467 } 468 469 470 /** 471 * Function implementing the recoup transaction: credits the old coin for 472 * every disclosed coin. IF it returns a non-error code, the transaction 473 * logic MUST NOT queue a MHD response. IF it returns a hard error, it 474 * sets the error state (and the reply is generated by the error phase). 475 * IF it returns the soft error code, the function MAY be called again to 476 * retry and MUST not queue a MHD response. 477 * 478 * @param cls a `struct RecoupRefreshContext *` 479 * @param connection MHD request which triggered the transaction 480 * @param[out] mhd_ret set to MHD response status for @a connection, 481 * if transaction failed (!) 482 * @return transaction status 483 */ 484 static enum GNUNET_DB_QueryStatus 485 recoup_transaction (void *cls, 486 struct MHD_Connection *connection, 487 enum MHD_Result *mhd_ret) 488 { 489 struct RecoupRefreshContext *wc = cls; 490 491 /* First, make sure all disclosed coins are known. This runs inside 492 of the transaction: on a conflict, the reply is queued and the 493 rollback removes every coin of this batch again. */ 494 { 495 const struct TALER_CoinPublicInfo *coins[wc->request.num_coins]; 496 uint64_t known_coin_ids[wc->request.num_coins]; 497 unsigned int num_disclosed = 0; 498 enum GNUNET_DB_QueryStatus qs; 499 500 for (size_t i = 0; i < wc->request.num_coins; i++) 501 { 502 const struct TEH_RecoupCoin *c = &wc->request.coins[i]; 503 504 if (c->disclosed) 505 coins[num_disclosed++] = &c->coin; 506 } 507 GNUNET_assert (0 < num_disclosed); 508 qs = TEH_make_coins_known (num_disclosed, 509 coins, 510 connection, 511 known_coin_ids, 512 mhd_ret); 513 if (qs < 0) 514 return qs; 515 num_disclosed = 0; 516 for (size_t i = 0; i < wc->request.num_coins; i++) 517 { 518 struct TEH_RecoupCoin *c = &wc->request.coins[i]; 519 520 if (c->disclosed) 521 c->known_coin_id = known_coin_ids[num_disclosed++]; 522 } 523 } 524 for (size_t i = 0; i < wc->request.num_coins; i++) 525 { 526 struct TEH_RecoupCoin *c = &wc->request.coins[i]; 527 enum GNUNET_DB_QueryStatus qs; 528 bool recoup_ok; 529 bool internal_failure; 530 531 if (! c->disclosed) 532 continue; 533 c->timestamp = wc->now; 534 qs = TALER_EXCHANGEDB_do_recoup_refresh (TEH_pg, 535 &wc->request.old_coin_pub, 536 wc->refresh.refresh_id, 537 (uint32_t) i, 538 &c->coin_blinding_secret, 539 &c->coin.coin_pub, 540 c->known_coin_id, 541 &c->coin_sig, 542 &c->timestamp, 543 &c->amount, 544 &recoup_ok, 545 &internal_failure); 546 if (0 > qs) 547 { 548 if (GNUNET_DB_STATUS_HARD_ERROR == qs) 549 SET_ERROR_WITH_DETAIL (wc, 550 RECOUP_REFRESH_ERROR_DB_FETCH_FAILED, 551 db_fetch_context, 552 "do_recoup_refresh"); 553 return qs; 554 } 555 if (internal_failure) 556 { 557 GNUNET_break (0); 558 SET_ERROR (wc, 559 RECOUP_REFRESH_ERROR_DB_INVARIANT_FAILURE); 560 return GNUNET_DB_STATUS_HARD_ERROR; 561 } 562 if (! recoup_ok) 563 { 564 /* The reply looks at the coin's history, so end our transaction. */ 565 TALER_EXCHANGEDB_rollback (TEH_pg); 566 SET_ERROR_WITH_DETAIL (wc, 567 RECOUP_REFRESH_ERROR_INSUFFICIENT_FUNDS, 568 insufficient_funds, 569 c); 570 return GNUNET_DB_STATUS_HARD_ERROR; 571 } 572 } 573 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 574 } 575 576 577 /** 578 * Run the main DB transaction. 579 * 580 * @param[in,out] wc context of the request 581 */ 582 static void 583 phase_run_transaction (struct RecoupRefreshContext *wc) 584 { 585 enum MHD_Result mhd_ret; 586 enum GNUNET_GenericReturnValue qs; 587 588 GNUNET_assert (RECOUP_REFRESH_PHASE_RUN_TRANSACTION == wc->phase); 589 qs = TEH_DB_run_transaction (wc->rc->connection, 590 "run recoup-refresh", 591 TEH_MT_REQUEST_OTHER, 592 &mhd_ret, 593 &recoup_transaction, 594 wc); 595 /* If the transaction has changed the phase, we don't alter it. */ 596 if (RECOUP_REFRESH_PHASE_RUN_TRANSACTION != wc->phase) 597 return; 598 if (GNUNET_OK != qs) 599 { 600 /* persistent soft error, reply already queued */ 601 finish_loop (wc, 602 mhd_ret); 603 return; 604 } 605 wc->phase++; 606 } 607 608 609 /** 610 * Generate the success response: the recouped coins and the batch 611 * confirmation signature. 612 * 613 * @param[in,out] wc context of the request 614 */ 615 static void 616 phase_generate_reply_success (struct RecoupRefreshContext *wc) 617 { 618 struct TALER_Amount total_amount; 619 struct GNUNET_HashCode h_recoups; 620 struct GNUNET_TIME_Timestamp timestamp; 621 struct TALER_ExchangePublicKeyP pub; 622 struct TALER_ExchangeSignatureP sig; 623 json_t *recoups; 624 enum TALER_ErrorCode ec; 625 626 if (GNUNET_OK != 627 TEH_recoup_summarize (wc->request.num_coins, 628 wc->request.coins, 629 &total_amount, 630 &h_recoups, 631 ×tamp, 632 &recoups)) 633 { 634 GNUNET_break (0); 635 SET_ERROR (wc, 636 RECOUP_REFRESH_ERROR_DB_INVARIANT_FAILURE); 637 return; 638 } 639 ec = TALER_exchange_online_confirm_recoup_refresh_batch_sign ( 640 &TEH_keys_exchange_sign_, 641 timestamp, 642 &wc->request.old_coin_pub, 643 &wc->request.rc, 644 &total_amount, 645 &h_recoups, 646 &pub, 647 &sig); 648 if (TALER_EC_NONE != ec) 649 { 650 json_decref (recoups); 651 SET_ERROR_WITH_DETAIL (wc, 652 RECOUP_REFRESH_ERROR_CONFIRMATION_SIGN, 653 ec_confirmation_sign, 654 ec); 655 return; 656 } 657 finish_loop (wc, 658 TALER_MHD_REPLY_JSON_PACK ( 659 wc->rc->connection, 660 MHD_HTTP_OK, 661 GNUNET_JSON_pack_data_auto ("old_coin_pub", 662 &wc->request.old_coin_pub), 663 GNUNET_JSON_pack_data_auto ("rc", 664 &wc->request.rc), 665 GNUNET_JSON_pack_timestamp ("timestamp", 666 timestamp), 667 TALER_JSON_pack_amount ("total_amount", 668 &total_amount), 669 GNUNET_JSON_pack_array_steal ("recoups", 670 recoups), 671 GNUNET_JSON_pack_data_auto ("exchange_sig", 672 &sig), 673 GNUNET_JSON_pack_data_auto ("exchange_pub", 674 &pub))); 675 } 676 677 678 /** 679 * Report the error in @a wc to the client. 680 * 681 * @param[in,out] wc context of the request 682 */ 683 static void 684 phase_generate_reply_error (struct RecoupRefreshContext *wc) 685 { 686 GNUNET_assert (RECOUP_REFRESH_PHASE_GENERATE_REPLY_ERROR == wc->phase); 687 switch (wc->error.code) 688 { 689 case RECOUP_REFRESH_ERROR_NONE: 690 break; 691 case RECOUP_REFRESH_ERROR_BATCH_SIZE_MISMATCH: 692 finish_loop (wc, 693 TALER_MHD_reply_with_error ( 694 wc->rc->connection, 695 MHD_HTTP_BAD_REQUEST, 696 TALER_EC_EXCHANGE_RECOUP_REFRESH_BATCH_SIZE_MISMATCH, 697 NULL)); 698 return; 699 case RECOUP_REFRESH_ERROR_BLINDING_FAILED: 700 finish_loop (wc, 701 TALER_MHD_reply_with_error ( 702 wc->rc->connection, 703 MHD_HTTP_INTERNAL_SERVER_ERROR, 704 TALER_EC_EXCHANGE_RECOUP_REFRESH_BLINDING_FAILED, 705 NULL)); 706 return; 707 case RECOUP_REFRESH_ERROR_COIN_SIGNATURE_INVALID: 708 finish_loop (wc, 709 TALER_MHD_reply_with_error ( 710 wc->rc->connection, 711 MHD_HTTP_FORBIDDEN, 712 TALER_EC_EXCHANGE_RECOUP_REFRESH_SIGNATURE_INVALID, 713 NULL)); 714 return; 715 case RECOUP_REFRESH_ERROR_COMMITMENT_MISMATCH: 716 finish_loop (wc, 717 TALER_MHD_reply_with_error ( 718 wc->rc->connection, 719 MHD_HTTP_CONFLICT, 720 TALER_EC_EXCHANGE_RECOUP_REFRESH_COMMITMENT_MISMATCH, 721 NULL)); 722 return; 723 case RECOUP_REFRESH_ERROR_CONFIRMATION_SIGN: 724 finish_loop (wc, 725 TALER_MHD_reply_with_ec ( 726 wc->rc->connection, 727 wc->error.details.ec_confirmation_sign, 728 NULL)); 729 return; 730 case RECOUP_REFRESH_ERROR_DB_FETCH_FAILED: 731 finish_loop (wc, 732 TALER_MHD_reply_with_error ( 733 wc->rc->connection, 734 MHD_HTTP_INTERNAL_SERVER_ERROR, 735 TALER_EC_GENERIC_DB_FETCH_FAILED, 736 wc->error.details.db_fetch_context)); 737 return; 738 case RECOUP_REFRESH_ERROR_DB_INVARIANT_FAILURE: 739 finish_loop (wc, 740 TALER_MHD_reply_with_error ( 741 wc->rc->connection, 742 MHD_HTTP_INTERNAL_SERVER_ERROR, 743 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 744 NULL)); 745 return; 746 case RECOUP_REFRESH_ERROR_DENOMINATION_EXPIRED: 747 finish_loop (wc, 748 TEH_RESPONSE_reply_expired_denom_pub_hash ( 749 wc->rc->connection, 750 wc->error.details.denom_h, 751 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, 752 "RECOUP-REFRESH")); 753 return; 754 case RECOUP_REFRESH_ERROR_DENOMINATION_KEY_UNKNOWN: 755 finish_loop (wc, 756 TEH_RESPONSE_reply_unknown_denom_pub_hash ( 757 wc->rc->connection, 758 wc->error.details.denom_h)); 759 return; 760 case RECOUP_REFRESH_ERROR_DENOMINATION_MISMATCH: 761 finish_loop (wc, 762 TALER_MHD_reply_with_error ( 763 wc->rc->connection, 764 MHD_HTTP_CONFLICT, 765 TALER_EC_EXCHANGE_RECOUP_REFRESH_DENOMINATION_MISMATCH, 766 NULL)); 767 return; 768 case RECOUP_REFRESH_ERROR_DENOMINATION_NOT_ELIGIBLE: 769 finish_loop (wc, 770 TEH_RESPONSE_reply_expired_denom_pub_hash ( 771 wc->rc->connection, 772 wc->error.details.denom_h, 773 TALER_EC_EXCHANGE_RECOUP_REFRESH_NOT_ELIGIBLE, 774 "RECOUP-REFRESH")); 775 return; 776 case RECOUP_REFRESH_ERROR_DENOMINATION_SIGNATURE_INVALID: 777 finish_loop (wc, 778 TALER_MHD_reply_with_error ( 779 wc->rc->connection, 780 MHD_HTTP_FORBIDDEN, 781 TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID, 782 NULL)); 783 return; 784 case RECOUP_REFRESH_ERROR_DENOMINATION_VALIDITY_IN_FUTURE: 785 finish_loop (wc, 786 TEH_RESPONSE_reply_expired_denom_pub_hash ( 787 wc->rc->connection, 788 wc->error.details.denom_h, 789 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, 790 "RECOUP-REFRESH")); 791 return; 792 case RECOUP_REFRESH_ERROR_INSUFFICIENT_FUNDS: 793 { 794 const struct TEH_RecoupCoin *c = wc->error.details.insufficient_funds; 795 796 finish_loop (wc, 797 TEH_RESPONSE_reply_coin_insufficient_funds ( 798 wc->rc->connection, 799 TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, 800 &c->coin.denom_pub_hash, 801 &c->coin.coin_pub)); 802 return; 803 } 804 case RECOUP_REFRESH_ERROR_KEYS_MISSING: 805 finish_loop (wc, 806 TALER_MHD_reply_with_error ( 807 wc->rc->connection, 808 MHD_HTTP_SERVICE_UNAVAILABLE, 809 TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, 810 NULL)); 811 return; 812 case RECOUP_REFRESH_ERROR_REQUEST_PARAMETER_MALFORMED: 813 finish_loop (wc, 814 TALER_MHD_reply_with_error ( 815 wc->rc->connection, 816 MHD_HTTP_BAD_REQUEST, 817 TALER_EC_GENERIC_PARAMETER_MALFORMED, 818 wc->error.details.request_parameter_malformed)); 819 return; 820 case RECOUP_REFRESH_ERROR_MELT_NOT_FOUND: 821 finish_loop (wc, 822 TALER_MHD_reply_with_error ( 823 wc->rc->connection, 824 MHD_HTTP_NOT_FOUND, 825 TALER_EC_EXCHANGE_RECOUP_REFRESH_MELT_NOT_FOUND, 826 NULL)); 827 return; 828 } 829 GNUNET_break (0); 830 finish_loop (wc, 831 MHD_NO); 832 } 833 834 835 /** 836 * Release the data of a refresh operation as returned by the database. 837 * 838 * @param[in,out] rf data to release 839 */ 840 static void 841 free_db_refresh_data (struct TALER_EXCHANGEDB_Refresh_vDOLDPLUS *rf) 842 { 843 if (NULL != rf->denom_sigs) 844 { 845 for (size_t i = 0; i < rf->num_coins; i++) 846 TALER_blinded_denom_sig_free (&rf->denom_sigs[i]); 847 GNUNET_free (rf->denom_sigs); 848 } 849 GNUNET_free (rf->denom_serials); 850 GNUNET_free (rf->cs_r_values); 851 GNUNET_free (rf->transfer_pubs); 852 GNUNET_free (rf->denom_pub_hashes); 853 } 854 855 856 /** 857 * Cleanup routine for the request, called upon completion. 858 * 859 * @param rc request context to clean up 860 */ 861 static void 862 clean_recoup_refresh_rc (struct TEH_RequestContext *rc) 863 { 864 struct RecoupRefreshContext *wc = rc->rh_ctx; 865 866 TEH_recoup_free_coins (wc->request.num_coins, 867 wc->request.coins); 868 if (wc->have_refresh) 869 free_db_refresh_data (&wc->refresh); 870 GNUNET_free (wc); 871 } 872 873 874 enum MHD_Result 875 TEH_handler_recoup_refresh ( 876 struct TEH_RequestContext *rc, 877 const json_t *root, 878 const char *const args[0]) 879 { 880 struct RecoupRefreshContext *wc = rc->rh_ctx; 881 882 (void) args; 883 if (NULL == wc) 884 { 885 wc = GNUNET_new (struct RecoupRefreshContext); 886 rc->rh_ctx = wc; 887 rc->rh_cleaner = &clean_recoup_refresh_rc; 888 wc->rc = rc; 889 wc->now = GNUNET_TIME_timestamp_get (); 890 } 891 while (true) 892 { 893 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 894 "recoup-refresh processing in phase %d\n", 895 wc->phase); 896 switch (wc->phase) 897 { 898 case RECOUP_REFRESH_PHASE_PARSE: 899 phase_parse (wc, 900 root); 901 break; 902 case RECOUP_REFRESH_PHASE_LOOKUP_OPERATION: 903 phase_lookup_operation (wc); 904 break; 905 case RECOUP_REFRESH_PHASE_CHECK_KEYS: 906 phase_check_keys (wc); 907 break; 908 case RECOUP_REFRESH_PHASE_VERIFY_COINS: 909 phase_verify_coins (wc); 910 break; 911 case RECOUP_REFRESH_PHASE_RUN_TRANSACTION: 912 phase_run_transaction (wc); 913 break; 914 case RECOUP_REFRESH_PHASE_GENERATE_REPLY_SUCCESS: 915 phase_generate_reply_success (wc); 916 break; 917 case RECOUP_REFRESH_PHASE_GENERATE_REPLY_ERROR: 918 phase_generate_reply_error (wc); 919 break; 920 case RECOUP_REFRESH_PHASE_RETURN_YES: 921 return MHD_YES; 922 case RECOUP_REFRESH_PHASE_RETURN_NO: 923 return MHD_NO; 924 } 925 } 926 } 927 928 929 /* end of taler-exchange-httpd_post-recoup-refresh.c */