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