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