taler-merchant-httpd_exchanges.c (35141B)
1 /* 2 This file is part of TALER 3 (C) 2014-2024 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 General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file src/backend/taler-merchant-httpd_exchanges.c 18 * @brief logic this HTTPD keeps for each exchange we interact with 19 * @author Marcello Stanisci 20 * @author Christian Grothoff 21 */ 22 #include "platform.h" 23 #include <taler/taler_json_lib.h> 24 #include <taler/taler_dbevents.h> 25 #include "taler-merchant-httpd_exchanges.h" 26 #include "taler-merchant-httpd.h" 27 #include "merchant-database/get_kyc_limits.h" 28 #include "merchant-database/set_instance.h" 29 #include "merchant-database/select_exchange_keys.h" 30 #include "merchant-database/event_listen.h" 31 #include "merchant-database/event_notify.h" 32 33 34 /** 35 * How often do we retry DB transactions with soft errors? 36 */ 37 #define MAX_RETRIES 3 38 39 /** 40 * Threshold after which exponential backoff should not increase. 41 */ 42 #define RETRY_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply ( \ 43 GNUNET_TIME_UNIT_SECONDS, 60) 44 45 /** 46 * This is how long /keys long-polls for, so we should 47 * allow this time between requests if there is no 48 * answer. See exchange_api_handle.c. 49 */ 50 #define LONG_POLL_THRESHOLD GNUNET_TIME_relative_multiply ( \ 51 GNUNET_TIME_UNIT_SECONDS, 120) 52 53 54 /** 55 * Information we keep for a pending #MMH_EXCHANGES_keys4exchange() operation. 56 */ 57 struct TMH_EXCHANGES_KeysOperation 58 { 59 60 /** 61 * Kept in a DLL. 62 */ 63 struct TMH_EXCHANGES_KeysOperation *next; 64 65 /** 66 * Kept in a DLL. 67 */ 68 struct TMH_EXCHANGES_KeysOperation *prev; 69 70 /** 71 * Function to call with the result. 72 */ 73 TMH_EXCHANGES_Find2Continuation fc; 74 75 /** 76 * Closure for @e fc. 77 */ 78 void *fc_cls; 79 80 /** 81 * Exchange we wait for the /keys for. 82 */ 83 struct TMH_Exchange *my_exchange; 84 85 /** 86 * Task scheduled to asynchronously return the result to 87 * the find continuation. 88 */ 89 struct GNUNET_SCHEDULER_Task *at; 90 91 }; 92 93 94 /** 95 * Information about wire transfer fees of an exchange, by wire method. 96 */ 97 struct FeesByWireMethod 98 { 99 100 /** 101 * Kept in a DLL. 102 */ 103 struct FeesByWireMethod *next; 104 105 /** 106 * Kept in a DLL. 107 */ 108 struct FeesByWireMethod *prev; 109 110 /** 111 * Wire method these fees are for. 112 */ 113 char *wire_method; 114 115 /** 116 * Applicable fees, NULL if unknown/error. 117 */ 118 struct TALER_EXCHANGE_WireAggregateFees *af; 119 120 }; 121 122 123 /** 124 * Internal representation for an exchange 125 */ 126 struct TMH_Exchange 127 { 128 129 /** 130 * Kept in a DLL. 131 */ 132 struct TMH_Exchange *next; 133 134 /** 135 * Kept in a DLL. 136 */ 137 struct TMH_Exchange *prev; 138 139 /** 140 * Head of FOs pending for this exchange. 141 */ 142 struct TMH_EXCHANGES_KeysOperation *keys_head; 143 144 /** 145 * Tail of FOs pending for this exchange. 146 */ 147 struct TMH_EXCHANGES_KeysOperation *keys_tail; 148 149 /** 150 * (base) URL of the exchange. 151 */ 152 char *url; 153 154 /** 155 * Currency offered by the exchange according to OUR configuration. 156 */ 157 char *currency; 158 159 /** 160 * The keys of this exchange. 161 */ 162 struct TALER_EXCHANGE_Keys *keys; 163 164 /** 165 * Head of wire fees from /wire request. 166 */ 167 struct FeesByWireMethod *wire_fees_head; 168 169 /** 170 * Tail of wire fees from /wire request. 171 */ 172 struct FeesByWireMethod *wire_fees_tail; 173 174 /** 175 * Task to retry downloading /keys again. 176 */ 177 struct GNUNET_SCHEDULER_Task *retry_task; 178 179 /** 180 * When are we willing to force downloading again? 181 */ 182 struct GNUNET_TIME_Absolute first_retry; 183 184 /** 185 * Current exponential back-off for @e retry_task. 186 */ 187 struct GNUNET_TIME_Relative retry_delay; 188 189 /** 190 * Master public key of the exchange. 191 */ 192 struct TALER_MasterPublicKeyP master_pub; 193 194 /** 195 * true if this exchange is from our configuration and 196 * explicitly trusted, false if we need to check each 197 * key to be sure it is trusted. 198 */ 199 bool trusted; 200 201 }; 202 203 204 /** 205 * Head of exchanges we know about. 206 */ 207 static struct TMH_Exchange *exchange_head; 208 209 /** 210 * Tail of exchanges we know about. 211 */ 212 static struct TMH_Exchange *exchange_tail; 213 214 /** 215 * Our event handler listening for /keys downloads 216 * being put into the database. 217 */ 218 static struct GNUNET_DB_EventHandler *keys_eh; 219 220 /** 221 * How many exchanges do we trust (for our configured 222 * currency) as per our configuration? Used for a 223 * sanity-check on startup. 224 */ 225 static int trusted_exchange_count; 226 227 228 const struct TALER_MasterPublicKeyP * 229 TMH_EXCHANGES_get_master_pub ( 230 const struct TMH_Exchange *exchange) 231 { 232 GNUNET_break ( (exchange->trusted) || 233 (NULL != exchange->keys) ); 234 return &exchange->master_pub; 235 } 236 237 238 const char * 239 TMH_EXCHANGES_get_currency ( 240 const struct TMH_Exchange *exchange) 241 { 242 return exchange->currency; 243 } 244 245 246 struct TMH_Exchange * 247 TMH_EXCHANGES_lookup_exchange (const char *exchange_url) 248 { 249 for (struct TMH_Exchange *exchange = exchange_head; 250 NULL != exchange; 251 exchange = exchange->next) 252 if (0 == strcmp (exchange->url, 253 exchange_url)) 254 return exchange; 255 return NULL; 256 } 257 258 259 bool 260 TMH_EXCHANGES_check_trusted ( 261 const char *exchange_url) 262 { 263 struct TMH_Exchange *exchange = TMH_EXCHANGES_lookup_exchange (exchange_url); 264 265 if (NULL == exchange) 266 return false; 267 return exchange->trusted; 268 } 269 270 271 /** 272 * Check if we have any remaining pending requests for the 273 * given @a exchange, and if we have the required data, call 274 * the callback. 275 * 276 * @param exchange the exchange to check for pending find operations 277 */ 278 static void 279 process_find_operations (struct TMH_Exchange *exchange) 280 { 281 struct GNUNET_TIME_Timestamp now; 282 283 now = GNUNET_TIME_timestamp_get (); 284 for (struct FeesByWireMethod *fbw = exchange->wire_fees_head; 285 NULL != fbw; 286 fbw = fbw->next) 287 { 288 while ( (NULL != fbw->af) && 289 (GNUNET_TIME_timestamp_cmp (fbw->af->end_date, 290 <, 291 now)) ) 292 { 293 struct TALER_EXCHANGE_WireAggregateFees *af = fbw->af; 294 295 fbw->af = af->next; 296 GNUNET_free (af); 297 } 298 if (NULL == fbw->af) 299 { 300 /* Disagreement on the current time */ 301 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 302 "Exchange has no wire fees configured for `%s' wire method\n", 303 fbw->wire_method); 304 } 305 else if (GNUNET_TIME_timestamp_cmp (fbw->af->start_date, 306 >, 307 now)) 308 { 309 /* Disagreement on the current time */ 310 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 311 "Exchange's earliest fee is %s ahead of our time. Clock skew issue?\n", 312 GNUNET_TIME_relative2s ( 313 GNUNET_TIME_absolute_get_remaining ( 314 fbw->af->start_date.abs_time), 315 true)); 316 } 317 } /* for all wire methods */ 318 319 { 320 struct TMH_EXCHANGES_KeysOperation *kon; 321 322 kon = NULL; 323 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 324 "Processing find operations for `%s'\n", 325 exchange->url); 326 for (struct TMH_EXCHANGES_KeysOperation *ko = exchange->keys_head; 327 NULL != ko; 328 ko = kon) 329 { 330 kon = ko->next; 331 ko->fc (ko->fc_cls, 332 exchange->keys, 333 exchange); 334 TMH_EXCHANGES_keys4exchange_cancel (ko); 335 } 336 } 337 } 338 339 340 /** 341 * Function called with information about the wire fees for each wire method. 342 * Stores the wire fees within our internal data structures for later use. 343 * 344 * @param exchange connection to the exchange 345 * @param master_pub public key of the exchange 346 * @param num_methods number of wire methods supported 347 * @param fbm wire fees by method 348 * @return #GNUNET_OK on success 349 */ 350 static enum GNUNET_GenericReturnValue 351 process_wire_fees ( 352 struct TMH_Exchange *exchange, 353 const struct TALER_MasterPublicKeyP *master_pub, 354 unsigned int num_methods, 355 const struct TALER_EXCHANGE_WireFeesByMethod fbm[static num_methods]) 356 { 357 for (unsigned int i = 0; i<num_methods; i++) 358 { 359 const char *wire_method = fbm[i].method; 360 const struct TALER_EXCHANGE_WireAggregateFees *fees = fbm[i].fees_head; 361 struct FeesByWireMethod *f; 362 struct TALER_EXCHANGE_WireAggregateFees *endp; 363 364 for (f = exchange->wire_fees_head; NULL != f; f = f->next) 365 if (0 == strcasecmp (wire_method, 366 f->wire_method)) 367 break; 368 if (NULL == f) 369 { 370 f = GNUNET_new (struct FeesByWireMethod); 371 f->wire_method = GNUNET_strdup (wire_method); 372 GNUNET_CONTAINER_DLL_insert (exchange->wire_fees_head, 373 exchange->wire_fees_tail, 374 f); 375 } 376 endp = f->af; 377 while ( (NULL != endp) && 378 (NULL != endp->next) ) 379 endp = endp->next; 380 while ( (NULL != endp) && 381 (NULL != fees) && 382 (GNUNET_TIME_timestamp_cmp (fees->start_date, 383 <, 384 endp->end_date)) ) 385 fees = fees->next; 386 if ( (NULL != endp) && 387 (NULL != fees) && 388 (GNUNET_TIME_timestamp_cmp (fees->start_date, 389 !=, 390 endp->end_date)) ) 391 { 392 /* Hole or overlap in the fee structure, not allowed! */ 393 GNUNET_break_op (0); 394 return GNUNET_SYSERR; 395 } 396 while (NULL != fees) 397 { 398 struct TALER_EXCHANGE_WireAggregateFees *af; 399 400 af = GNUNET_new (struct TALER_EXCHANGE_WireAggregateFees); 401 *af = *fees; 402 af->next = NULL; 403 if (NULL == endp) 404 f->af = af; 405 else 406 endp->next = af; 407 endp = af; 408 fees = fees->next; 409 } /* all fees for this method */ 410 } /* for all methods (i) */ 411 return GNUNET_OK; 412 } 413 414 415 /** 416 * Retry getting keys from the given exchange in the closure. 417 * 418 * @param cls the `struct TMH_Exchange *` 419 */ 420 static void 421 retry_exchange (void *cls) 422 { 423 struct TMH_Exchange *exchange = cls; 424 struct GNUNET_DB_EventHeaderP es = { 425 .size = htons (sizeof (es)), 426 .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_FORCE_KEYS) 427 }; 428 429 exchange->retry_task = NULL; 430 exchange->retry_delay 431 = GNUNET_TIME_randomized_backoff (exchange->retry_delay, 432 RETRY_BACKOFF_THRESHOLD); 433 exchange->first_retry 434 = GNUNET_TIME_relative_to_absolute ( 435 exchange->retry_delay); 436 437 TALER_MERCHANTDB_event_notify (TMH_db, 438 &es, 439 exchange->url, 440 strlen (exchange->url) + 1); 441 } 442 443 444 /** 445 * Task to asynchronously return keys operation result to caller. 446 * 447 * @param cls a `struct TMH_EXCHANGES_KeysOperation` 448 */ 449 static void 450 return_keys (void *cls) 451 { 452 struct TMH_EXCHANGES_KeysOperation *fo = cls; 453 struct TMH_Exchange *exchange = fo->my_exchange; 454 455 fo->at = NULL; 456 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 457 "Returning key data for %s instantly\n", 458 exchange->url); 459 process_find_operations (exchange); 460 } 461 462 463 struct TMH_EXCHANGES_KeysOperation * 464 TMH_EXCHANGES_keys4exchange ( 465 const char *chosen_exchange, 466 bool force_download, 467 TMH_EXCHANGES_Find2Continuation fc, 468 void *fc_cls) 469 { 470 struct TMH_Exchange *exchange; 471 struct TMH_EXCHANGES_KeysOperation *fo; 472 473 if (NULL == TMH_curl_ctx) 474 { 475 GNUNET_break (0); 476 return NULL; 477 } 478 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 479 "Trying to find chosen exchange `%s'\n", 480 chosen_exchange); 481 exchange = TMH_EXCHANGES_lookup_exchange (chosen_exchange); 482 if (NULL == exchange) 483 { 484 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 485 "Exchange `%s' not configured\n", 486 chosen_exchange); 487 return NULL; 488 } 489 if (! exchange->trusted) 490 { 491 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 492 "Exchange `%s' not trusted\n", 493 chosen_exchange); 494 return NULL; 495 } 496 fo = GNUNET_new (struct TMH_EXCHANGES_KeysOperation); 497 fo->fc = fc; 498 fo->fc_cls = fc_cls; 499 fo->my_exchange = exchange; 500 GNUNET_CONTAINER_DLL_insert (exchange->keys_head, 501 exchange->keys_tail, 502 fo); 503 if ( (NULL == exchange->keys) && 504 (! force_download) ) 505 { 506 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 507 "Waiting for `%skeys' already, failing query instantly\n", 508 exchange->url); 509 GNUNET_assert (NULL == fo->at); 510 fo->at = GNUNET_SCHEDULER_add_now (&return_keys, 511 fo); 512 return fo; 513 } 514 if ( (NULL != exchange->keys) && 515 (! force_download) && 516 (GNUNET_TIME_absolute_is_future ( 517 exchange->keys->key_data_expiration.abs_time)) ) 518 { 519 /* We have a valid reply, immediately return result */ 520 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 521 "The exchange `%s' is ready\n", 522 exchange->url); 523 GNUNET_assert (NULL == fo->at); 524 fo->at = GNUNET_SCHEDULER_add_now (&return_keys, 525 fo); 526 return fo; 527 } 528 if ( (force_download) && 529 (GNUNET_TIME_absolute_is_future (exchange->first_retry)) && 530 (NULL != exchange->keys) ) 531 { 532 /* Return results immediately. */ 533 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 534 "Earliest retry is in the future, returning keys now\n"); 535 fo->at = GNUNET_SCHEDULER_add_now (&return_keys, 536 fo); 537 /* *no* return here, we MAY schedule a 'retry_task' in the 538 next block if there isn't one yet */ 539 } 540 if (NULL == exchange->retry_task) 541 exchange->retry_task 542 = GNUNET_SCHEDULER_add_at (exchange->first_retry, 543 &retry_exchange, 544 exchange); 545 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 546 "Next %skeys request scheduled for %s\n", 547 exchange->url, 548 GNUNET_TIME_absolute2s ( 549 exchange->first_retry)); 550 /* No activity to launch, we are already doing so. */ 551 return fo; 552 } 553 554 555 void 556 TMH_EXCHANGES_keys4exchange_cancel ( 557 struct TMH_EXCHANGES_KeysOperation *fo) 558 { 559 struct TMH_Exchange *exchange = fo->my_exchange; 560 561 if (NULL != fo->at) 562 { 563 GNUNET_SCHEDULER_cancel (fo->at); 564 fo->at = NULL; 565 } 566 GNUNET_CONTAINER_DLL_remove (exchange->keys_head, 567 exchange->keys_tail, 568 fo); 569 GNUNET_free (fo); 570 } 571 572 573 /** 574 * Obtain applicable fees for @a exchange and @a wire_method. 575 * 576 * @param exchange the exchange to query 577 * @param now current time 578 * @param wire_method the wire method we want the fees for 579 * @return NULL if we do not have fees for this method yet 580 */ 581 static const struct FeesByWireMethod * 582 get_wire_fees (const struct TMH_Exchange *exchange, 583 struct GNUNET_TIME_Timestamp now, 584 const char *wire_method) 585 { 586 for (struct FeesByWireMethod *fbw = exchange->wire_fees_head; 587 NULL != fbw; 588 fbw = fbw->next) 589 { 590 if (0 == strcasecmp (fbw->wire_method, 591 wire_method) ) 592 { 593 struct TALER_EXCHANGE_WireAggregateFees *af; 594 595 /* Advance through list up to current time */ 596 while ( (NULL != (af = fbw->af)) && 597 (GNUNET_TIME_timestamp_cmp (now, 598 >=, 599 af->end_date)) ) 600 { 601 fbw->af = af->next; 602 GNUNET_free (af); 603 } 604 return fbw; 605 } 606 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 607 "Exchange supports `%s' as a wire method (but we do not use that one)\n", 608 fbw->wire_method); 609 } 610 return NULL; 611 } 612 613 614 /** 615 * Free @a exchange. 616 * 617 * @param[in] exchange entry to free 618 */ 619 static void 620 free_exchange_entry (struct TMH_Exchange *exchange) 621 { 622 struct FeesByWireMethod *f; 623 624 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 625 "Releasing %s exchange %s\n", 626 exchange->trusted ? "trusted" : "untrusted", 627 exchange->url); 628 GNUNET_CONTAINER_DLL_remove (exchange_head, 629 exchange_tail, 630 exchange); 631 while (NULL != (f = exchange->wire_fees_head)) 632 { 633 struct TALER_EXCHANGE_WireAggregateFees *af; 634 635 GNUNET_CONTAINER_DLL_remove (exchange->wire_fees_head, 636 exchange->wire_fees_tail, 637 f); 638 while (NULL != (af = f->af)) 639 { 640 f->af = af->next; 641 GNUNET_free (af); 642 } 643 GNUNET_free (f->wire_method); 644 GNUNET_free (f); 645 } 646 TALER_EXCHANGE_keys_decref (exchange->keys); 647 exchange->keys = NULL; 648 if (NULL != exchange->retry_task) 649 { 650 GNUNET_SCHEDULER_cancel (exchange->retry_task); 651 exchange->retry_task = NULL; 652 } 653 GNUNET_assert (NULL == exchange->keys_head); 654 GNUNET_assert (NULL == exchange->keys_tail); 655 GNUNET_free (exchange->currency); 656 GNUNET_free (exchange->url); 657 GNUNET_free (exchange); 658 } 659 660 661 enum GNUNET_GenericReturnValue 662 TMH_EXCHANGES_lookup_wire_fee ( 663 const struct TMH_Exchange *exchange, 664 const char *wire_method, 665 struct TALER_Amount *wire_fee) 666 { 667 const struct FeesByWireMethod *fbm; 668 const struct TALER_EXCHANGE_WireAggregateFees *af; 669 670 fbm = get_wire_fees (exchange, 671 GNUNET_TIME_timestamp_get (), 672 wire_method); 673 if (NULL == fbm) 674 return GNUNET_NO; 675 af = fbm->af; 676 if (NULL == af) 677 return GNUNET_NO; 678 *wire_fee = af->fees.wire; 679 return GNUNET_OK; 680 } 681 682 683 enum TMH_ExchangeStatus 684 TMH_exchange_check_debit ( 685 const char *instance_id, 686 const struct TMH_Exchange *exchange, 687 const struct TMH_WireMethod *wm, 688 struct TALER_Amount *max_amount) 689 { 690 const struct TALER_EXCHANGE_Keys *keys = exchange->keys; 691 bool have_kyc = false; 692 bool no_access_token = true; 693 enum TMH_ExchangeStatus retry_ok = 0; 694 695 if (GNUNET_TIME_absolute_is_past (exchange->first_retry)) 696 retry_ok = TMH_ES_RETRY_OK; 697 698 if (NULL == keys) 699 return TMH_ES_NO_KEYS | retry_ok; 700 if (0 != strcasecmp (keys->currency, 701 max_amount->currency)) 702 { 703 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 704 "Currency mismatch: exchange %s uses %s, we need %s\n", 705 exchange->url, 706 keys->currency, 707 max_amount->currency); 708 return TMH_ES_NO_CURR | retry_ok; 709 } 710 { 711 struct TALER_NormalizedPayto np; 712 bool account_ok; 713 714 np = TALER_payto_normalize (wm->payto_uri); 715 account_ok = TALER_EXCHANGE_keys_test_account_allowed (keys, 716 false, 717 np); 718 GNUNET_free (np.normalized_payto); 719 if (! account_ok) 720 return TMH_ES_NO_ACC | retry_ok; 721 } 722 if (! keys->kyc_enabled) 723 return TMH_ES_OK | retry_ok; 724 725 { 726 json_t *jlimits = NULL; 727 enum GNUNET_DB_QueryStatus qs; 728 729 qs = TALER_MERCHANTDB_set_instance ( 730 TMH_db, 731 instance_id); 732 if (0 >= qs) 733 { 734 GNUNET_break (0); 735 return TMH_ES_NO_KEYS | TMH_ES_RETRY_OK; 736 } 737 qs = TALER_MERCHANTDB_get_kyc_limits (TMH_db, 738 wm->payto_uri, 739 instance_id, 740 exchange->url, 741 &have_kyc, 742 &no_access_token, 743 &jlimits); 744 GNUNET_break (qs >= 0); 745 GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 746 TALER_MERCHANTDB_set_instance ( 747 TMH_db, 748 NULL)); 749 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 750 "get_kyc_limits for %s at %s returned %s/%s\n", 751 wm->payto_uri.full_payto, 752 exchange->url, 753 have_kyc ? "KYC OK" : "KYC missing", 754 NULL == jlimits ? "default limits" : "custom limits"); 755 if ( (qs > 0) && 756 (NULL != jlimits) ) 757 { 758 json_t *jlimit; 759 size_t idx; 760 struct TALER_Amount kyc_limit; 761 bool unlimited = true; 762 763 json_array_foreach (jlimits, idx, jlimit) 764 { 765 enum TALER_KYCLOGIC_KycTriggerEvent ot; 766 struct TALER_Amount threshold; 767 bool soft_limit = false; 768 struct GNUNET_JSON_Specification spec[] = { 769 TALER_JSON_spec_kycte ("operation_type", 770 &ot), 771 TALER_JSON_spec_amount_any ("threshold", 772 &threshold), 773 GNUNET_JSON_spec_mark_optional ( 774 GNUNET_JSON_spec_bool ("soft_limit", 775 &soft_limit), 776 NULL), 777 GNUNET_JSON_spec_end () 778 }; 779 780 if (GNUNET_OK != 781 GNUNET_JSON_parse (jlimit, 782 spec, 783 NULL, NULL)) 784 { 785 GNUNET_break (0); 786 continue; 787 } 788 if (soft_limit) 789 continue; 790 if ( (TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT != ot) && 791 (TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION != ot) ) 792 continue; 793 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 794 "KYC rule %u with limit %s applies\n", 795 (unsigned int) idx, 796 TALER_amount2s (&threshold)); 797 if (unlimited) 798 { 799 unlimited = false; 800 kyc_limit = threshold; 801 } 802 else 803 { 804 TALER_amount_min (&kyc_limit, 805 &kyc_limit, 806 &threshold); 807 } 808 } 809 json_decref (jlimits); 810 /* We had custom rules, do not evaluate default rules */ 811 if (! unlimited) 812 TALER_amount_min (max_amount, 813 max_amount, 814 &kyc_limit); 815 return TMH_ES_OK | retry_ok; 816 } /* END of if qs > 0, NULL != jlimits */ 817 } 818 819 /* Check zero limits *only* if we did no KYC process at all yet. 820 Because if we did, there is at least a chance that those have 821 been lifted. */ 822 if ( (no_access_token) || 823 ( (! have_kyc) && 824 (TALER_EXCHANGE_keys_evaluate_zero_limits ( 825 keys, 826 TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) || 827 TALER_EXCHANGE_keys_evaluate_zero_limits ( 828 keys, 829 TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION)) ) ) 830 { 831 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 832 "KYC requirements of %s not satisfied\n", 833 exchange->url); 834 GNUNET_assert (GNUNET_OK == 835 TALER_amount_set_zero ( 836 max_amount->currency, 837 max_amount)); 838 return TMH_ES_OK | retry_ok; 839 } 840 /* In any case, abide by hard limits (unless we have custom rules). */ 841 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 842 "Evaluating default hard limits of %s\n", 843 exchange->url); 844 TALER_EXCHANGE_keys_evaluate_hard_limits ( 845 keys, 846 TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT, 847 max_amount); 848 TALER_EXCHANGE_keys_evaluate_hard_limits ( 849 keys, 850 TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION, 851 max_amount); 852 if (TALER_EXCHANGE_keys_evaluate_zero_limits ( 853 keys, 854 TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) || 855 TALER_EXCHANGE_keys_evaluate_zero_limits ( 856 keys, 857 TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION)) 858 { 859 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 860 "Operation is zero-limited by default\n"); 861 GNUNET_assert (GNUNET_OK == 862 TALER_amount_set_zero (max_amount->currency, 863 max_amount)); 864 } 865 return TMH_ES_OK | retry_ok; 866 } 867 868 869 void 870 TMH_exchange_get_trusted (TMH_ExchangeCallback cb, 871 void *cb_cls) 872 { 873 for (const struct TMH_Exchange *exchange = exchange_head; 874 NULL != exchange; 875 exchange = exchange->next) 876 { 877 if (! exchange->trusted) 878 { 879 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 880 "Exchange %s not trusted, skipping!\n", 881 exchange->url); 882 continue; 883 } 884 cb (cb_cls, 885 exchange->url, 886 exchange); 887 } 888 } 889 890 891 bool 892 TMH_test_exchange_configured_for_currency ( 893 const char *currency) 894 { 895 for (const struct TMH_Exchange *exchange = exchange_head; 896 NULL != exchange; 897 exchange = exchange->next) 898 { 899 if (! exchange->trusted) 900 continue; 901 if (NULL == exchange->currency) 902 continue; 903 if (0 == strcmp (currency, 904 exchange->currency)) 905 return true; 906 } 907 return false; 908 } 909 910 911 /** 912 * (Re)load of keys from DB. 913 * 914 * @param exchange exchange to reload keys of 915 */ 916 static void 917 reload_exchange_keys (struct TMH_Exchange *exchange) 918 { 919 enum GNUNET_DB_QueryStatus qs; 920 struct TALER_EXCHANGE_Keys *keys; 921 struct GNUNET_TIME_Absolute first_retry; 922 923 qs = TALER_MERCHANTDB_select_exchange_keys (TMH_db, 924 exchange->url, 925 &first_retry, 926 &keys); 927 if (qs < 0) 928 { 929 GNUNET_break (0); 930 return; 931 } 932 if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) || 933 (NULL == keys) ) 934 { 935 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 936 "No keys yet for `%s'\n", 937 exchange->url); 938 return; 939 } 940 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 941 "Loading latest keys of `%s' from database\n", 942 exchange->url); 943 exchange->retry_delay = GNUNET_TIME_UNIT_ZERO; 944 exchange->first_retry = first_retry; 945 if (NULL == exchange->currency) 946 exchange->currency = GNUNET_strdup (keys->currency); 947 if (0 != strcmp (keys->currency, 948 exchange->currency)) 949 { 950 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 951 "/keys cached in our database are for currency `%s', but we expected `%s'\n", 952 keys->currency, 953 exchange->currency); 954 return; 955 } 956 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 957 "Loaded /keys from database with %u accounts, %u fees\n", 958 keys->accounts_len, 959 keys->fees_len); 960 if (GNUNET_OK != 961 process_wire_fees (exchange, 962 &keys->master_pub, 963 keys->fees_len, 964 keys->fees)) 965 { 966 /* invalid wire fee specification given */ 967 GNUNET_break_op (0); 968 /* but: we can continue anyway, things may just not 969 work, but probably better than to not keep going. */ 970 return; 971 } 972 973 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 974 "Reloaded /keys of %s from database\n", 975 exchange->url); 976 TALER_EXCHANGE_keys_decref (exchange->keys); 977 exchange->keys = keys; 978 if ( (exchange->trusted) && 979 (0 != GNUNET_memcmp (&exchange->master_pub, 980 &keys->master_pub)) ) 981 { 982 /* master pub differs => do not trust the exchange (without auditor) */ 983 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 984 "Master public key of exchange `%s' differs from our configuration. Not trusting exchange.\n", 985 exchange->url); 986 exchange->trusted = false; 987 } 988 if (! exchange->trusted) 989 { 990 exchange->master_pub = keys->master_pub; 991 for (struct TMH_Exchange *e = exchange_head; 992 NULL != e; 993 e = e->next) 994 { 995 if (e == exchange) 996 continue; 997 if (! e->trusted) 998 continue; 999 if (0 == 1000 GNUNET_memcmp (&e->master_pub, 1001 &exchange->master_pub)) 1002 exchange->trusted = true; /* same exchange, different URL => trust applies */ 1003 } 1004 } 1005 1006 process_find_operations (exchange); 1007 } 1008 1009 1010 /** 1011 * Function called on each configuration section. Finds sections 1012 * about exchanges, parses the entries. 1013 * 1014 * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *` 1015 * @param section name of the section 1016 */ 1017 static void 1018 accept_exchanges (void *cls, 1019 const char *section) 1020 { 1021 const struct GNUNET_CONFIGURATION_Handle *cfg = cls; 1022 char *url; 1023 char *mks; 1024 struct TMH_Exchange *exchange; 1025 char *currency; 1026 1027 if (GNUNET_SYSERR == trusted_exchange_count) 1028 return; 1029 if (0 != strncasecmp (section, 1030 "merchant-exchange-", 1031 strlen ("merchant-exchange-"))) 1032 return; 1033 if (GNUNET_YES == 1034 GNUNET_CONFIGURATION_get_value_yesno (cfg, 1035 section, 1036 "DISABLED")) 1037 return; 1038 if (GNUNET_OK != 1039 GNUNET_CONFIGURATION_get_value_string (cfg, 1040 section, 1041 "EXCHANGE_BASE_URL", 1042 &url)) 1043 { 1044 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1045 section, 1046 "EXCHANGE_BASE_URL"); 1047 return; 1048 } 1049 exchange = TMH_EXCHANGES_lookup_exchange (url); 1050 if (NULL != exchange) 1051 { 1052 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1053 section, 1054 "EXCHANGE_BASE_URL", 1055 "same base URL specified again"); 1056 GNUNET_free (url); 1057 return; 1058 } 1059 if (GNUNET_OK != 1060 GNUNET_CONFIGURATION_get_value_string (cfg, 1061 section, 1062 "CURRENCY", 1063 ¤cy)) 1064 { 1065 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1066 section, 1067 "CURRENCY"); 1068 GNUNET_free (url); 1069 return; 1070 } 1071 exchange = GNUNET_new (struct TMH_Exchange); 1072 exchange->url = url; 1073 exchange->currency = currency; 1074 GNUNET_CONTAINER_DLL_insert (exchange_head, 1075 exchange_tail, 1076 exchange); 1077 if (GNUNET_OK == 1078 GNUNET_CONFIGURATION_get_value_string (cfg, 1079 section, 1080 "MASTER_KEY", 1081 &mks)) 1082 { 1083 if (GNUNET_OK == 1084 GNUNET_CRYPTO_eddsa_public_key_from_string ( 1085 mks, 1086 strlen (mks), 1087 &exchange->master_pub.eddsa_pub)) 1088 { 1089 exchange->trusted = true; 1090 trusted_exchange_count++; 1091 } 1092 else 1093 { 1094 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1095 section, 1096 "MASTER_KEY", 1097 "malformed EdDSA key"); 1098 } 1099 GNUNET_free (mks); 1100 } 1101 else 1102 { 1103 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1104 "MASTER_KEY missing in section '%s', not trusting exchange\n", 1105 section); 1106 } 1107 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1108 "Setup exchange %s as %s\n", 1109 exchange->url, 1110 exchange->trusted ? "trusted" : "untrusted"); 1111 reload_exchange_keys (exchange); 1112 if (NULL != exchange->retry_task) 1113 { 1114 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1115 "Exchange at `%s' configured in multiple configuration sections (see `%s')!\n", 1116 exchange->url, 1117 section); 1118 trusted_exchange_count = GNUNET_SYSERR; 1119 return; 1120 } 1121 exchange->retry_task 1122 = GNUNET_SCHEDULER_add_now (&retry_exchange, 1123 exchange); 1124 } 1125 1126 1127 /** 1128 * Trigger (re)loading of keys from DB. 1129 * 1130 * @param cls NULL 1131 * @param extra base URL of the exchange that changed 1132 * @param extra_len number of bytes in @a extra 1133 */ 1134 static void 1135 update_exchange_keys (void *cls, 1136 const void *extra, 1137 size_t extra_len) 1138 { 1139 const char *url = extra; 1140 struct TMH_Exchange *exchange; 1141 1142 if ( (NULL == extra) || 1143 (0 == extra_len) ) 1144 { 1145 GNUNET_break (0); 1146 return; 1147 } 1148 if ('\0' != url[extra_len - 1]) 1149 { 1150 GNUNET_break (0); 1151 return; 1152 } 1153 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1154 "Received keys change notification: reload `%s'\n", 1155 url); 1156 exchange = TMH_EXCHANGES_lookup_exchange (url); 1157 GNUNET_break (NULL != exchange); 1158 if (NULL != exchange) 1159 reload_exchange_keys (exchange); 1160 } 1161 1162 1163 bool 1164 TMH_EXCHANGES_is_below_limit ( 1165 const struct TALER_EXCHANGE_Keys *keys, 1166 enum TALER_KYCLOGIC_KycTriggerEvent operation_type, 1167 const struct TALER_Amount *amount) 1168 { 1169 if (NULL == keys) 1170 { 1171 /* should only be called after we have keys! */ 1172 GNUNET_break (0); 1173 return false; 1174 } 1175 for (unsigned int i = 0; i<keys->hard_limits_length; i++) 1176 { 1177 const struct TALER_EXCHANGE_AccountLimit *al 1178 = &keys->hard_limits[i]; 1179 1180 if (operation_type != al->operation_type) 1181 continue; 1182 if (-1 == 1183 TALER_amount_cmp (&al->threshold, 1184 amount)) 1185 /* -1: threshold < amount */ 1186 return false; 1187 } 1188 return true; 1189 } 1190 1191 1192 void 1193 TMH_EXCHANGES_get_limit ( 1194 const char *exchange_url, 1195 enum TALER_KYCLOGIC_KycTriggerEvent operation_type, 1196 struct TALER_Amount *amount) 1197 { 1198 struct TMH_Exchange *exchange; 1199 const struct TALER_EXCHANGE_Keys *keys; 1200 1201 exchange = TMH_EXCHANGES_lookup_exchange (exchange_url); 1202 if ( (NULL == exchange) || 1203 (NULL == (keys = exchange->keys)) ) 1204 { 1205 GNUNET_assert (GNUNET_OK == 1206 TALER_amount_set_zero ( 1207 amount->currency, 1208 amount)); 1209 return; 1210 } 1211 for (unsigned int i = 0; i<keys->hard_limits_length; i++) 1212 { 1213 const struct TALER_EXCHANGE_AccountLimit *al 1214 = &keys->hard_limits[i]; 1215 1216 if (operation_type != al->operation_type) 1217 continue; 1218 TALER_amount_min (amount, 1219 amount, 1220 &al->threshold); 1221 } 1222 } 1223 1224 1225 enum GNUNET_GenericReturnValue 1226 TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg) 1227 { 1228 /* get exchanges from the merchant configuration and try to connect to them */ 1229 { 1230 struct GNUNET_DB_EventHeaderP es = { 1231 .size = htons (sizeof (es)), 1232 .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS) 1233 }; 1234 1235 GNUNET_assert (NULL == keys_eh); 1236 keys_eh = TALER_MERCHANTDB_event_listen (TMH_db, 1237 &es, 1238 GNUNET_TIME_UNIT_FOREVER_REL, 1239 &update_exchange_keys, 1240 NULL); 1241 } 1242 GNUNET_CONFIGURATION_iterate_sections (cfg, 1243 &accept_exchanges, 1244 (void *) cfg); 1245 /* build JSON with list of trusted exchanges (will be included in contracts) */ 1246 return trusted_exchange_count; 1247 } 1248 1249 1250 void 1251 TMH_EXCHANGES_done () 1252 { 1253 if (NULL != keys_eh) 1254 { 1255 TALER_MERCHANTDB_event_listen_cancel (keys_eh); 1256 keys_eh = NULL; 1257 } 1258 while (NULL != exchange_head) 1259 free_exchange_entry (exchange_head); 1260 } 1261 1262 1263 /* end of taler-merchant-httpd_get-exchanges.c */