taler-merchant-kyccheck.c (61175B)
1 /* 2 This file is part of TALER 3 Copyright (C) 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 Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file src/backend/taler-merchant-kyccheck.c 18 * @brief Process that check the KYC status of our bank accounts at all exchanges 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 struct Inquiry; 23 #define TALER_EXCHANGE_GET_KYC_CHECK_RESULT_CLOSURE struct Inquiry 24 #define TALER_EXCHANGE_GET_KYC_INFO_RESULT_CLOSURE struct Inquiry 25 #define TALER_EXCHANGE_POST_KYC_UPLOAD_RESULT_CLOSURE struct Inquiry 26 #include "microhttpd.h" 27 #include <gnunet/gnunet_util_lib.h> 28 #include <jansson.h> 29 #include <pthread.h> 30 #include <regex.h> 31 #include <taler/taler_dbevents.h> 32 #include <taler/taler_json_lib.h> 33 #include <taler/taler_exchange_service.h> 34 #include <taler/exchange/post-kyc-upload-ID.h> 35 #include "taler/taler_merchant_util.h" 36 #include "taler/taler_merchant_bank_lib.h" 37 #include "merchantdb_lib.h" 38 #include "merchantdb_lib.h" 39 #include "merchant-database/account_kyc_get_outdated.h" 40 #include "merchant-database/account_kyc_set_status.h" 41 #include "merchant-database/delete_tos_accepted_early.h" 42 #include "merchant-database/get_kyc_status.h" 43 #include "merchant-database/lookup_tos_accepted_early.h" 44 #include "merchant-database/set_instance.h" 45 #include "merchant-database/select_accounts.h" 46 #include "merchant-database/select_exchange_keys.h" 47 #include "merchant-database/event_listen.h" 48 #include "merchant-database/preflight.h" 49 #include "merchant-database/start.h" 50 51 52 /** 53 * Timeout for the exchange interaction. Rather long as we should do 54 * long-polling and do not want to wake up too often. 55 */ 56 #define EXCHANGE_TIMEOUT GNUNET_TIME_relative_multiply ( \ 57 GNUNET_TIME_UNIT_MINUTES, \ 58 30) 59 60 /** 61 * How long do we wait between requests if all we wait 62 * for is a change in the AML investigation status? 63 * Default value. 64 */ 65 #define AML_FREQ GNUNET_TIME_relative_multiply ( \ 66 GNUNET_TIME_UNIT_HOURS, \ 67 6) 68 69 /** 70 * How long do we wait between requests if all we wait 71 * for is a change in the AML investigation status? 72 */ 73 static struct GNUNET_TIME_Relative aml_freq; 74 75 /** 76 * How frequently do we check for updates to our KYC status 77 * if there is no actual reason to check? Set to a very low 78 * frequency, just to ensure we eventually notice. 79 * Default value. 80 */ 81 #define AML_LOW_FREQ GNUNET_TIME_relative_multiply ( \ 82 GNUNET_TIME_UNIT_DAYS, \ 83 7) 84 85 /** 86 * How frequently do we check for updates to our KYC status 87 * if there is no actual reason to check? Set to a very low 88 * frequency, just to ensure we eventually notice. 89 */ 90 static struct GNUNET_TIME_Relative aml_low_freq; 91 92 93 /** 94 * How many inquiries do we process concurrently at most. 95 */ 96 #define OPEN_INQUIRY_LIMIT 1024 97 98 /** 99 * Name of the KYC form (``FORM_ID``) the exchange uses to affirm 100 * acceptance of the terms of service. Must match the value submitted 101 * by #TALER_EXCHANGE_post_kyc_upload_accept_tos_create(). 102 */ 103 #define ACCEPT_TOS_FORM "accept-tos" 104 105 /** 106 * Minimum delay before we retry after the exchange returned an 107 * internal error to our attempt to automatically accept the terms 108 * of service on behalf of the user. 109 */ 110 #define TOS_ERROR_RETRY_DELAY GNUNET_TIME_UNIT_HOURS 111 112 113 /** 114 * Information about an exchange. 115 */ 116 struct Exchange 117 { 118 /** 119 * Kept in a DLL. 120 */ 121 struct Exchange *next; 122 123 /** 124 * Kept in a DLL. 125 */ 126 struct Exchange *prev; 127 128 /** 129 * The keys of this exchange 130 */ 131 struct TALER_EXCHANGE_Keys *keys; 132 133 }; 134 135 136 /** 137 * Information about an Account. 138 */ 139 struct Account 140 { 141 /** 142 * Kept in a DLL. 143 */ 144 struct Account *next; 145 146 /** 147 * Kept in a DLL. 148 */ 149 struct Account *prev; 150 151 /** 152 * Head of inquiries for this account. 153 */ 154 struct Inquiry *i_head; 155 156 /** 157 * Tail of inquiries for this account. 158 */ 159 struct Inquiry *i_tail; 160 161 /** 162 * Merchant instance this account belongs to. 163 */ 164 char *instance_id; 165 166 /** 167 * The payto-URI of this account. 168 */ 169 struct TALER_FullPayto merchant_account_uri; 170 171 /** 172 * Wire hash of the merchant bank account (with the 173 * respective salt). 174 */ 175 struct TALER_MerchantWireHashP h_wire; 176 177 /** 178 * Private key of the instance. 179 */ 180 union TALER_AccountPrivateKeyP ap; 181 182 /** 183 * Hash of the @e merchant_account_uri. 184 */ 185 struct TALER_NormalizedPaytoHashP h_payto; 186 187 /** 188 * Database generation when this account 189 * was last active. 190 */ 191 uint64_t account_gen; 192 193 }; 194 195 196 /** 197 * Information about an inquiry job. 198 */ 199 struct Inquiry 200 { 201 /** 202 * Kept in a DLL. 203 */ 204 struct Inquiry *next; 205 206 /** 207 * Kept in a DLL. 208 */ 209 struct Inquiry *prev; 210 211 /** 212 * Main task for this inquiry. 213 */ 214 struct GNUNET_SCHEDULER_Task *task; 215 216 /** 217 * Which exchange is this inquiry about. 218 */ 219 struct Exchange *e; 220 221 /** 222 * Which account is this inquiry about. 223 */ 224 struct Account *a; 225 226 /** 227 * AccountLimits that apply to the account, NULL 228 * if unknown. 229 */ 230 json_t *jlimits; 231 232 /** 233 * Handle for the actual HTTP request to the exchange. 234 */ 235 struct TALER_EXCHANGE_GetKycCheckHandle *kyc; 236 237 /** 238 * Handle for fetching /kyc-info to discover the upload ID used to 239 * automatically accept the terms of service, NULL if not active. 240 */ 241 struct TALER_EXCHANGE_GetKycInfoHandle *kyc_info; 242 243 /** 244 * Handle for the /kyc-upload request submitting the automatic 245 * terms-of-service acceptance, NULL if not active. 246 */ 247 struct TALER_EXCHANGE_PostKycUploadHandle *tos_upload; 248 249 /** 250 * If non-NULL, the ``Taler-Terms-Version`` of the terms of service 251 * that the user accepted early (via ``POST /private/accept-tos-early``) 252 * and that we are trying to submit to the exchange on their behalf. 253 * Owned by the inquiry. 254 */ 255 char *tos_etag; 256 257 /** 258 * Access token for the /kyc-info API. 259 */ 260 struct TALER_AccountAccessTokenP access_token; 261 262 /** 263 * Last time we called the /kyc-check endpoint. 264 */ 265 struct GNUNET_TIME_Timestamp last_kyc_check; 266 267 /** 268 * When is the next KYC check due? 269 */ 270 struct GNUNET_TIME_Absolute due; 271 272 /** 273 * When should the current KYC time out? 274 */ 275 struct GNUNET_TIME_Absolute timeout; 276 277 /** 278 * Current exponential backoff. 279 */ 280 struct GNUNET_TIME_Relative backoff; 281 282 /** 283 * Rule generation known to the client, 0 for none. 284 * Corresponds to the decision row in the exchange. 285 */ 286 uint64_t rule_gen; 287 288 /** 289 * Last HTTP status returned by the exchange from 290 * the /kyc-check endpoint. 291 */ 292 unsigned int last_http_status; 293 294 /** 295 * Last Taler error code returned by the exchange from 296 * the /kyc-check endpoint. 297 */ 298 enum TALER_ErrorCode last_ec; 299 300 /** 301 * True if this is not our first time we make this request. 302 */ 303 bool not_first_time; 304 305 /** 306 * Do soft limits on transactions apply to this merchant for operations 307 * merchants care about? If so, we should increase our request frequency 308 * and ask more often to see if they were lifted. 309 */ 310 bool zero_limited; 311 312 /** 313 * Did we not run this inquiry due to limits? 314 */ 315 bool limited; 316 317 /** 318 * Do we believe this account's KYC is in good shape? 319 */ 320 bool kyc_ok; 321 322 /** 323 * True if merchant did perform this account's KYC AUTH transfer and @e access_token is set. 324 */ 325 bool auth_ok; 326 327 /** 328 * True if the account is known to be currently under 329 * investigation by AML staff. 330 */ 331 bool aml_review; 332 333 }; 334 335 336 /** 337 * Head of known exchanges. 338 */ 339 static struct Exchange *e_head; 340 341 /** 342 * Tail of known exchanges. 343 */ 344 static struct Exchange *e_tail; 345 346 /** 347 * Head of accounts. 348 */ 349 static struct Account *a_head; 350 351 /** 352 * Tail of accounts. 353 */ 354 static struct Account *a_tail; 355 356 /** 357 * The merchant's configuration. 358 */ 359 static const struct GNUNET_CONFIGURATION_Handle *cfg; 360 361 /** 362 * Our database connection. 363 */ 364 static struct TALER_MERCHANTDB_PostgresContext *pg; 365 366 /** 367 * Handle to the context for interacting with the bank. 368 */ 369 static struct GNUNET_CURL_Context *ctx; 370 371 /** 372 * Scheduler context for running the @e ctx. 373 */ 374 static struct GNUNET_CURL_RescheduleContext *rc; 375 376 /** 377 * Event handler to learn that there may be new bank 378 * accounts to check. 379 */ 380 static struct GNUNET_DB_EventHandler *eh_accounts; 381 382 /** 383 * Event handler to learn that there may be new exchange 384 * keys to check. 385 */ 386 static struct GNUNET_DB_EventHandler *eh_keys; 387 388 /** 389 * Event handler to learn that there was a KYC 390 * rule triggered and we need to check the KYC 391 * status for an account. 392 */ 393 static struct GNUNET_DB_EventHandler *eh_rule; 394 395 /** 396 * Event handler to learn that higher-frequency KYC 397 * checks were forced by an application actively inspecting 398 * some KYC status values. 399 */ 400 static struct GNUNET_DB_EventHandler *eh_update_forced; 401 402 /** 403 * Event handler to learn that we got new /keys 404 * from an exchange and should reconsider eligibility. 405 */ 406 static struct GNUNET_DB_EventHandler *keys_rule; 407 408 /** 409 * Main task to discover (new) accounts. 410 */ 411 static struct GNUNET_SCHEDULER_Task *account_task; 412 413 /** 414 * Counter determining how often we have called 415 * "select_accounts" on the database. 416 */ 417 static uint64_t database_gen; 418 419 /** 420 * How many active inquiries do we have right now. 421 */ 422 static unsigned int active_inquiries; 423 424 /** 425 * Value to return from main(). 0 on success, non-zero on errors. 426 */ 427 static int global_ret; 428 429 /** 430 * #GNUNET_YES if we are in test mode and should exit when idle. 431 */ 432 static int test_mode; 433 434 /** 435 * True if the last DB query was limited by the 436 * #OPEN_INQUIRY_LIMIT and we thus should check again 437 * as soon as we are substantially below that limit, 438 * and not only when we get a DB notification. 439 */ 440 static bool at_limit; 441 442 443 /** 444 * Check about performing a /kyc-check request with the 445 * exchange for the given inquiry. 446 * 447 * @param cls a `struct Inquiry` to process 448 */ 449 static void 450 inquiry_work (void *cls); 451 452 453 /** 454 * An inquiry finished, check if we should resume others. 455 */ 456 static void 457 end_inquiry (void) 458 { 459 GNUNET_assert (active_inquiries > 0); 460 active_inquiries--; 461 if ( (active_inquiries < OPEN_INQUIRY_LIMIT / 2) && 462 (at_limit) ) 463 { 464 at_limit = false; 465 for (struct Account *a = a_head; 466 NULL != a; 467 a = a->next) 468 { 469 for (struct Inquiry *i = a->i_head; 470 NULL != i; 471 i = i->next) 472 { 473 if (! i->limited) 474 continue; 475 GNUNET_assert (NULL == i->task); 476 /* done synchronously so that the active_inquiries 477 is updated immediately */ 478 inquiry_work (i); 479 if (at_limit) 480 break; 481 } 482 if (at_limit) 483 break; 484 } 485 } 486 if ( (! at_limit) && 487 (0 == active_inquiries) && 488 (test_mode) ) 489 { 490 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 491 "No more open inquiries and in test mode. Existing.\n"); 492 GNUNET_SCHEDULER_shutdown (); 493 return; 494 } 495 } 496 497 498 /** 499 * Pack the given @a limit into the JSON @a limits array. 500 * 501 * @param limit account limit to pack 502 * @param[in,out] limits JSON array to extend 503 */ 504 static void 505 pack_limit (const struct TALER_EXCHANGE_AccountLimit *limit, 506 json_t *limits) 507 { 508 json_t *jl; 509 510 jl = GNUNET_JSON_PACK ( 511 TALER_JSON_pack_kycte ("operation_type", 512 limit->operation_type), 513 GNUNET_JSON_pack_time_rel ("timeframe", 514 limit->timeframe), 515 TALER_JSON_pack_amount ("threshold", 516 &limit->threshold), 517 GNUNET_JSON_pack_bool ("soft_limit", 518 limit->soft_limit) 519 ); 520 GNUNET_assert (0 == 521 json_array_append_new (limits, 522 jl)); 523 } 524 525 526 /** 527 * Update KYC status for @a i based on 528 * @a account_kyc_status 529 * 530 * @param[in,out] i inquiry context, jlimits is updated 531 * @param account_kyc_status account KYC status details 532 */ 533 static void 534 store_kyc_status ( 535 struct Inquiry *i, 536 const struct TALER_EXCHANGE_AccountKycStatus *account_kyc_status) 537 { 538 json_t *jlimits; 539 540 json_decref (i->jlimits); 541 jlimits = json_array (); 542 GNUNET_assert (NULL != jlimits); 543 i->zero_limited = false; 544 for (unsigned int j = 0; j<account_kyc_status->limits_length; j++) 545 { 546 const struct TALER_EXCHANGE_AccountLimit *limit 547 = &account_kyc_status->limits[j]; 548 549 pack_limit (limit, 550 jlimits); 551 if (TALER_amount_is_zero (&limit->threshold) && 552 limit->soft_limit && 553 ( (TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT == limit->operation_type) || 554 (TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE == limit->operation_type) || 555 (TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION == limit->operation_type) ) ) 556 { 557 i->zero_limited = true; 558 } 559 } 560 i->jlimits = jlimits; 561 GNUNET_break (! GNUNET_is_zero (&account_kyc_status->access_token)); 562 i->access_token = account_kyc_status->access_token; 563 i->aml_review = account_kyc_status->aml_review; 564 i->kyc_ok = (MHD_HTTP_OK == i->last_http_status); 565 } 566 567 568 /** 569 * The current interaction with the exchange for inquiry @a i is 570 * complete (or was aborted). Schedule the next periodic KYC check 571 * at @a i->due and release the active-inquiry slot. 572 * 573 * @param[in,out] i the inquiry to reschedule 574 */ 575 static void 576 finish_inquiry (struct Inquiry *i) 577 { 578 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 579 "Will repeat inquiry in %s\n", 580 GNUNET_TIME_relative2s ( 581 GNUNET_TIME_absolute_get_remaining (i->due), 582 true)); 583 if (! GNUNET_TIME_absolute_is_never (i->due)) 584 i->task = GNUNET_SCHEDULER_add_at (i->due, 585 &inquiry_work, 586 i); 587 end_inquiry (); 588 } 589 590 591 /** 592 * Clear the tos-accepted data from the user, we do not 593 * need the flag anymore, either because we passed it on 594 * to the exchange or because they are too old. 595 * 596 * @param i inquiry this is about 597 */ 598 static void 599 clear_tos (const struct Inquiry *i) 600 { 601 enum GNUNET_DB_QueryStatus qs; 602 603 qs = TALER_MERCHANTDB_set_instance (pg, 604 i->a->instance_id); 605 if (qs < 0) 606 { 607 GNUNET_break (0); 608 global_ret = EXIT_FAILURE; 609 GNUNET_SCHEDULER_shutdown (); 610 return; 611 } 612 qs = TALER_MERCHANTDB_delete_tos_accepted_early ( 613 pg, 614 i->a->instance_id, 615 i->e->keys->exchange_url); 616 GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 617 TALER_MERCHANTDB_set_instance (pg, 618 NULL)); 619 if (qs < 0) 620 { 621 GNUNET_break (0); 622 global_ret = EXIT_FAILURE; 623 GNUNET_SCHEDULER_shutdown (); 624 return; 625 } 626 } 627 628 629 /** 630 * Function called with the result of submitting an automatic 631 * terms-of-service acceptance to the exchange via /kyc-upload. 632 * 633 * @param i the inquiry the acceptance was for 634 * @param pr the exchange's response 635 */ 636 static void 637 tos_upload_cb (struct Inquiry *i, 638 const struct TALER_EXCHANGE_PostKycUploadResponse *pr) 639 { 640 unsigned int http_status = pr->hr.http_status; 641 642 i->tos_upload = NULL; 643 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 644 "Automatic ToS acceptance for `%s' at `%s' returned HTTP %u\n", 645 i->a->merchant_account_uri.full_payto, 646 i->e->keys->exchange_url, 647 http_status); 648 switch (http_status) 649 { 650 case MHD_HTTP_OK: 651 case MHD_HTTP_NO_CONTENT: 652 /* Exchange accepted the terms of service: re-check KYC now. */ 653 i->due = GNUNET_TIME_UNIT_ZERO_ABS; 654 clear_tos (i); 655 break; 656 case 0: /* no answer, like network failure */ 657 case MHD_HTTP_INTERNAL_SERVER_ERROR: 658 case MHD_HTTP_BAD_GATEWAY: 659 case MHD_HTTP_REQUEST_ENTITY_TOO_LARGE: /* Wild error */ 660 /* Internal/transient error at the exchange: do NOT clear the early 661 acceptance, but back off for at least an hour before retrying 662 with a regular periodic KYC check. */ 663 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 664 "Exchange `%s' failed to process automatic ToS acceptance (HTTP %u); retrying later\n", 665 i->e->keys->exchange_url, 666 http_status); 667 i->due = GNUNET_TIME_relative_to_absolute ( 668 GNUNET_TIME_randomize (TOS_ERROR_RETRY_DELAY)); 669 break; 670 case MHD_HTTP_NOT_FOUND: 671 /* Something must have changed exchange-side, try again 672 immediately, but do not clear ToS acceptance */ 673 i->due = GNUNET_TIME_UNIT_ZERO_ABS; 674 clear_tos (i); 675 break; 676 case MHD_HTTP_BAD_REQUEST: 677 /* This should not happen, go back to manual KYC */ 678 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 679 "Exchange `%s' failed to process automatic ToS acceptance (HTTP %u); retrying later\n", 680 i->e->keys->exchange_url, 681 http_status); 682 i->due = GNUNET_TIME_relative_to_absolute ( 683 GNUNET_TIME_randomize (TOS_ERROR_RETRY_DELAY)); 684 break; 685 case MHD_HTTP_CONFLICT: 686 /* Exchange rejected the accepted ToS version (ETag not acceptable 687 or ToS acceptance disappeared): 688 clear the early acceptance so we do not loop, then re-check KYC 689 (the user will have to accept the ToS through the regular flow 690 if it still applies). */ 691 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 692 "Exchange `%s' rejected early ToS acceptance (version `%s', HTTP %u); clearing early acceptance\n", 693 i->e->keys->exchange_url, 694 i->tos_etag, 695 http_status); 696 clear_tos (i); 697 i->due = GNUNET_TIME_UNIT_ZERO_ABS; 698 break; 699 } 700 GNUNET_free (i->tos_etag); 701 finish_inquiry (i); 702 } 703 704 705 /** 706 * Submit an automatic terms-of-service acceptance for inquiry @a i to 707 * the exchange, using the @a id of the corresponding KYC requirement 708 * (obtained from /kyc-info) and the early-accepted version in 709 * @a i->tos_etag. 710 * 711 * @param[in,out] i inquiry to submit the ToS acceptance for 712 * @param id KYC requirement / upload ID for the terms-of-service form 713 */ 714 static void 715 start_tos_upload (struct Inquiry *i, 716 const char *id) 717 { 718 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 719 "Submitting automatic ToS acceptance (version `%s', id `%s') to `%s'\n", 720 i->tos_etag, 721 id, 722 i->e->keys->exchange_url); 723 i->tos_upload = TALER_EXCHANGE_post_kyc_upload_accept_tos_create ( 724 ctx, 725 i->e->keys->exchange_url, 726 id, 727 i->tos_etag); 728 if ( (NULL == i->tos_upload) || 729 (TALER_EC_NONE != 730 TALER_EXCHANGE_post_kyc_upload_start (i->tos_upload, 731 &tos_upload_cb, 732 i)) ) 733 { 734 GNUNET_break (0); 735 if (NULL != i->tos_upload) 736 { 737 TALER_EXCHANGE_post_kyc_upload_cancel (i->tos_upload); 738 i->tos_upload = NULL; 739 } 740 /* Could not even start the upload: treat as transient, keep the 741 early acceptance and retry with a regular periodic check. */ 742 GNUNET_free (i->tos_etag); 743 finish_inquiry (i); 744 } 745 } 746 747 748 /** 749 * Function called with the result of fetching /kyc-info while trying 750 * to automatically accept the terms of service. Finds the ID of the 751 * terms-of-service requirement and submits the acceptance. 752 * 753 * @param i the inquiry the lookup was for 754 * @param ir the exchange's response 755 */ 756 static void 757 tos_info_cb (struct Inquiry *i, 758 const struct TALER_EXCHANGE_GetKycInfoResponse *ir) 759 { 760 i->kyc_info = NULL; 761 if (MHD_HTTP_OK == ir->hr.http_status) 762 { 763 const char *id = NULL; 764 765 for (size_t j = 0; j < ir->details.ok.requirements_length; j++) 766 { 767 const struct TALER_EXCHANGE_RequirementInformation *req 768 = &ir->details.ok.requirements[j]; 769 770 if ( (NULL != req->form) && 771 (NULL != req->id) && 772 (0 == strcmp (req->form, 773 ACCEPT_TOS_FORM)) ) 774 { 775 id = req->id; 776 break; 777 } 778 } 779 if (NULL != id) 780 { 781 start_tos_upload (i, 782 id); 783 return; 784 } 785 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 786 "No `%s' requirement at `%s'; cannot auto-accept ToS, falling back to periodic check\n", 787 ACCEPT_TOS_FORM, 788 i->e->keys->exchange_url); 789 } 790 else 791 { 792 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 793 "GET /kyc-info at `%s' returned HTTP %u; cannot auto-accept ToS now\n", 794 i->e->keys->exchange_url, 795 ir->hr.http_status); 796 } 797 /* Could not determine the upload ID: keep the early acceptance and 798 retry on the next regular periodic KYC check. */ 799 GNUNET_free (i->tos_etag); 800 finish_inquiry (i); 801 } 802 803 804 /** 805 * Start the automatic terms-of-service acceptance for inquiry @a i by 806 * fetching /kyc-info to discover the ID of the terms-of-service 807 * requirement. The early-accepted version is in @a i->tos_etag. 808 * 809 * @param[in,out] i inquiry to auto-accept the terms of service for 810 */ 811 static void 812 start_tos_info (struct Inquiry *i) 813 { 814 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 815 "Fetching /kyc-info from `%s' to auto-accept ToS for `%s'\n", 816 i->e->keys->exchange_url, 817 i->a->merchant_account_uri.full_payto); 818 i->kyc_info = TALER_EXCHANGE_get_kyc_info_create (ctx, 819 i->e->keys->exchange_url, 820 &i->access_token); 821 if ( (NULL == i->kyc_info) || 822 (TALER_EC_NONE != 823 TALER_EXCHANGE_get_kyc_info_start (i->kyc_info, 824 &tos_info_cb, 825 i)) ) 826 { 827 GNUNET_break (0); 828 if (NULL != i->kyc_info) 829 { 830 TALER_EXCHANGE_get_kyc_info_cancel (i->kyc_info); 831 i->kyc_info = NULL; 832 } 833 /* Could not start the lookup: keep the early acceptance and retry 834 with a regular periodic check. */ 835 GNUNET_free (i->tos_etag); 836 finish_inquiry (i); 837 } 838 } 839 840 841 /** 842 * The exchange asked us (via @a tos_required) to accept its terms of 843 * service. Check whether the user already accepted the terms of 844 * service early (via ``POST /private/accept-tos-early``). If so, 845 * remember the accepted version in @a i->tos_etag so that we will try 846 * to submit it to the exchange automatically. 847 * 848 * @param[in,out] i inquiry for which the exchange requires ToS acceptance 849 * @param req required ETag for the accepted ToS 850 */ 851 static void 852 check_early_tos_acceptance (struct Inquiry *i, 853 const char *req) 854 { 855 enum GNUNET_DB_QueryStatus qs; 856 char *tos_version = NULL; 857 858 qs = TALER_MERCHANTDB_set_instance (pg, 859 i->a->instance_id); 860 if (qs < 0) 861 { 862 GNUNET_break (0); 863 global_ret = EXIT_FAILURE; 864 GNUNET_SCHEDULER_shutdown (); 865 return; 866 } 867 qs = TALER_MERCHANTDB_lookup_tos_accepted_early (pg, 868 i->a->instance_id, 869 i->e->keys->exchange_url, 870 &tos_version); 871 GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 872 TALER_MERCHANTDB_set_instance (pg, 873 NULL)); 874 if (qs < 0) 875 { 876 GNUNET_break (0); 877 global_ret = EXIT_FAILURE; 878 GNUNET_SCHEDULER_shutdown (); 879 return; 880 } 881 if (NULL == tos_version) 882 { 883 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 884 "Exchange `%s' supports early ToS acceptance, but user did not accept ToS early\n", 885 i->e->keys->exchange_url); 886 return; 887 } 888 if (0 != strcmp (tos_version, 889 req)) 890 { 891 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 892 "User accepted outdated ToS version `%s' early, but exchange wants `%s'. User will need to accept the ToS again!\n", 893 tos_version, 894 req); 895 GNUNET_free (tos_version); 896 return; 897 } 898 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 899 "User accepted ToS version `%s' early; will submit to `%s'\n", 900 tos_version, 901 i->e->keys->exchange_url); 902 GNUNET_free (i->tos_etag); 903 i->tos_etag = tos_version; 904 } 905 906 907 /** 908 * Function called with the result of a KYC check. 909 * 910 * @param cls a `struct Inquiry *` 911 * @param ks the account's KYC status details 912 */ 913 static void 914 exchange_check_cb ( 915 struct Inquiry *i, 916 const struct TALER_EXCHANGE_GetKycCheckResponse *ks) 917 { 918 bool progress = false; 919 920 i->kyc = NULL; 921 if (! i->not_first_time) 922 progress = true; 923 if ( (i->last_http_status != ks->hr.http_status) && 924 (0 != ks->hr.http_status) ) 925 progress = true; 926 if (0 != ks->hr.http_status) 927 { 928 i->last_http_status = ks->hr.http_status; 929 i->last_ec = ks->hr.ec; 930 } 931 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 932 "KYC status of `%s' / %s at `%s' is %u\n", 933 i->a->merchant_account_uri.full_payto, 934 i->a->instance_id, 935 i->e->keys->exchange_url, 936 ks->hr.http_status); 937 switch (ks->hr.http_status) 938 { 939 case 0: 940 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 941 "Exchange did not responded to /kyc-check request!\n"); 942 i->backoff 943 = GNUNET_TIME_randomized_backoff (i->backoff, 944 EXCHANGE_TIMEOUT); 945 i->due = GNUNET_TIME_relative_to_absolute (i->backoff); 946 break; 947 case MHD_HTTP_OK: 948 if (i->rule_gen != ks->details.ok.rule_gen) 949 progress = true; 950 i->rule_gen = ks->details.ok.rule_gen; 951 i->last_kyc_check = GNUNET_TIME_timestamp_get (); 952 /* exchange says KYC is OK, gives status information */ 953 i->auth_ok = true; 954 store_kyc_status (i, 955 &ks->details.ok); 956 i->backoff = GNUNET_TIME_UNIT_MINUTES; 957 if (i->aml_review || i->zero_limited) 958 { 959 if (! progress) 960 i->due = GNUNET_TIME_relative_to_absolute ( 961 GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_freq, 962 i->backoff))); 963 } 964 else 965 { 966 /* KYC is OK, only check again if triggered */ 967 if (! progress) 968 i->due = GNUNET_TIME_relative_to_absolute ( 969 GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_low_freq, 970 i->backoff))); 971 } 972 break; 973 case MHD_HTTP_ACCEPTED: 974 if (i->rule_gen != ks->details.accepted.rule_gen) 975 progress = true; 976 i->rule_gen = ks->details.accepted.rule_gen; 977 i->last_kyc_check = GNUNET_TIME_timestamp_get (); 978 /* exchange says KYC is required */ 979 i->auth_ok = true; 980 store_kyc_status (i, 981 &ks->details.accepted); 982 i->backoff = GNUNET_TIME_UNIT_MINUTES; 983 /* Start immediately with long-polling */ 984 if (! progress) 985 i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time, 986 i->timeout); 987 if (NULL != ks->details.accepted.tos_required) 988 { 989 /* Exchange wants the user to accept its terms of service. 990 If the user already accepted them early, try to submit that 991 acceptance to the exchange automatically. */ 992 check_early_tos_acceptance (i, 993 ks->details.accepted.tos_required); 994 } 995 break; 996 case MHD_HTTP_NO_CONTENT: 997 i->rule_gen = 0; 998 i->last_kyc_check = GNUNET_TIME_timestamp_get (); 999 i->backoff = GNUNET_TIME_UNIT_MINUTES; 1000 /* exchange claims KYC is off! */ 1001 i->kyc_ok = true; 1002 i->aml_review = false; 1003 /* Clear limits, in case exchange had KYC on previously */ 1004 json_decref (i->jlimits); 1005 i->jlimits = NULL; 1006 /* KYC is OK, only check again if triggered */ 1007 i->due = GNUNET_TIME_relative_to_absolute ( 1008 GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_low_freq, 1009 i->backoff))); 1010 break; 1011 case MHD_HTTP_FORBIDDEN: /* bad signature */ 1012 i->rule_gen = 0; 1013 i->last_kyc_check = GNUNET_TIME_timestamp_get (); 1014 /* Forbidden => KYC auth must be wrong */ 1015 i->auth_ok = false; 1016 /* Start with long-polling */ 1017 if (! progress) 1018 i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time, 1019 i->timeout); 1020 i->backoff = GNUNET_TIME_UNIT_MINUTES; 1021 break; 1022 case MHD_HTTP_NOT_FOUND: /* account unknown */ 1023 i->rule_gen = 0; 1024 i->last_kyc_check = GNUNET_TIME_timestamp_get (); 1025 /* Account unknown => no KYC auth yet */ 1026 i->auth_ok = false; 1027 /* unknown account => wire transfer required! */ 1028 i->kyc_ok = false; 1029 /* There should not be any limits yet, but clear them 1030 just in case the exchange has amnesia */ 1031 json_decref (i->jlimits); 1032 i->jlimits = NULL; 1033 /* Start immediately with Long-polling */ 1034 if (! progress) 1035 i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time, 1036 i->timeout); 1037 i->backoff = GNUNET_TIME_UNIT_MINUTES; 1038 break; 1039 case MHD_HTTP_CONFLICT: /* no account_pub known */ 1040 i->rule_gen = 0; 1041 i->last_kyc_check = GNUNET_TIME_timestamp_get (); 1042 /* Conflict => KYC auth wire transfer missing! */ 1043 i->auth_ok = false; 1044 /* Start immediately with Long-polling */ 1045 if (! progress) 1046 i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time, 1047 i->timeout); 1048 i->backoff = GNUNET_TIME_UNIT_MINUTES; 1049 break; 1050 default: 1051 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1052 "Exchange responded with HTTP status %u (%d) to /kyc-check request!\n", 1053 ks->hr.http_status, 1054 ks->hr.ec); 1055 i->backoff 1056 = GNUNET_TIME_randomized_backoff (i->backoff, 1057 EXCHANGE_TIMEOUT); 1058 i->last_kyc_check = GNUNET_TIME_timestamp_get (); 1059 i->due = GNUNET_TIME_relative_to_absolute (i->backoff); 1060 i->auth_ok = false; 1061 break; 1062 } 1063 1064 { 1065 enum GNUNET_DB_QueryStatus qs; 1066 1067 qs = TALER_MERCHANTDB_set_instance (pg, 1068 i->a->instance_id); 1069 if (qs < 0) 1070 { 1071 GNUNET_break (0); 1072 global_ret = EXIT_FAILURE; 1073 GNUNET_SCHEDULER_shutdown (); 1074 return; 1075 } 1076 qs = TALER_MERCHANTDB_account_kyc_set_status ( 1077 pg, 1078 i->a->instance_id, 1079 &i->a->h_wire, 1080 i->e->keys->exchange_url, 1081 i->last_kyc_check, 1082 i->due, 1083 i->backoff, 1084 i->last_http_status, 1085 i->last_ec, 1086 i->rule_gen, 1087 (i->auth_ok) 1088 ? &i->access_token 1089 : NULL, 1090 i->jlimits, 1091 i->aml_review, 1092 i->kyc_ok); 1093 GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 1094 TALER_MERCHANTDB_set_instance ( 1095 pg, 1096 NULL)); 1097 if (qs < 0) 1098 { 1099 GNUNET_break (0); 1100 global_ret = EXIT_FAILURE; 1101 GNUNET_SCHEDULER_shutdown (); 1102 return; 1103 } 1104 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1105 "account_kyc_set_status (%s, %s, %u, %s, %s) returned %d\n", 1106 i->a->instance_id, 1107 i->e->keys->exchange_url, 1108 i->last_http_status, 1109 i->auth_ok ? "auth OK" : "auth needed", 1110 NULL == i->jlimits ? "default limits" : "custom limits", 1111 (int) qs); 1112 i->not_first_time = true; 1113 } 1114 if (NULL != i->tos_etag) 1115 { 1116 /* The user accepted the terms of service early and the exchange now 1117 requires acceptance: try to submit it automatically (this keeps 1118 the active-inquiry slot) instead of waiting for the next check. */ 1119 start_tos_info (i); 1120 return; 1121 } 1122 finish_inquiry (i); 1123 } 1124 1125 1126 static void 1127 inquiry_work (void *cls) 1128 { 1129 struct Inquiry *i = cls; 1130 enum TALER_EXCHANGE_KycLongPollTarget lpt; 1131 1132 i->task = NULL; 1133 if (! GNUNET_TIME_absolute_is_past (i->due)) 1134 { 1135 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1136 "Will start inquiry on %s for %s in %s\n", 1137 i->a->merchant_account_uri.full_payto, 1138 i->e->keys->exchange_url, 1139 GNUNET_TIME_relative2s ( 1140 GNUNET_TIME_absolute_get_remaining (i->due), 1141 true)); 1142 i->task 1143 = GNUNET_SCHEDULER_add_at (i->due, 1144 &inquiry_work, 1145 i); 1146 goto finish; 1147 } 1148 1149 GNUNET_assert (OPEN_INQUIRY_LIMIT >= active_inquiries); 1150 if (OPEN_INQUIRY_LIMIT <= active_inquiries) 1151 { 1152 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1153 "Not looking for work: at limit\n"); 1154 i->limited = true; 1155 at_limit = true; 1156 return; 1157 } 1158 at_limit = false; 1159 i->timeout 1160 = GNUNET_TIME_relative_to_absolute (EXCHANGE_TIMEOUT); 1161 lpt = TALER_EXCHANGE_KLPT_NONE; 1162 if (! i->auth_ok) 1163 lpt = TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER; 1164 else if (! i->kyc_ok) 1165 lpt = TALER_EXCHANGE_KLPT_KYC_OK; 1166 else if (i->aml_review) 1167 lpt = TALER_EXCHANGE_KLPT_INVESTIGATION_DONE; 1168 if (! i->not_first_time) 1169 lpt = TALER_EXCHANGE_KLPT_NONE; /* no long polling on 1st call */ 1170 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1171 "Starting KYC status of `%s' for %s at `%s' (%d, %d, %d) using LPT %d\n", 1172 i->a->merchant_account_uri.full_payto, 1173 i->a->instance_id, 1174 i->e->keys->exchange_url, 1175 i->not_first_time, 1176 i->auth_ok, 1177 i->kyc_ok, 1178 lpt); 1179 i->kyc = TALER_EXCHANGE_get_kyc_check_create ( 1180 ctx, 1181 i->e->keys->exchange_url, 1182 &i->a->h_payto, 1183 &i->a->ap); 1184 if (NULL == i->kyc) 1185 { 1186 GNUNET_break (0); 1187 i->due = i->timeout; 1188 i->task 1189 = GNUNET_SCHEDULER_add_at (i->due, 1190 &inquiry_work, 1191 i); 1192 goto finish; 1193 } 1194 GNUNET_assert (GNUNET_OK == 1195 TALER_EXCHANGE_get_kyc_check_set_options ( 1196 i->kyc, 1197 TALER_EXCHANGE_get_kyc_check_option_known_rule_gen ( 1198 i->rule_gen), 1199 TALER_EXCHANGE_get_kyc_check_option_lpt (lpt), 1200 TALER_EXCHANGE_get_kyc_check_option_timeout ( 1201 i->not_first_time && (! test_mode) 1202 ? EXCHANGE_TIMEOUT 1203 : GNUNET_TIME_UNIT_ZERO))); 1204 GNUNET_assert (TALER_EC_NONE == 1205 TALER_EXCHANGE_get_kyc_check_start (i->kyc, 1206 &exchange_check_cb, 1207 i)); 1208 active_inquiries++; 1209 finish: 1210 if ( (0 == active_inquiries) && 1211 (test_mode) ) 1212 { 1213 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1214 "No more open inquiries and in test mode. Existing.\n"); 1215 GNUNET_SCHEDULER_shutdown (); 1216 return; 1217 } 1218 } 1219 1220 1221 /** 1222 * Check if the account @a could work with exchange that 1223 * has keys @a keys. 1224 * 1225 * @param keys the keys of an exchange 1226 * @param a an account 1227 */ 1228 static bool 1229 is_eligible (const struct TALER_EXCHANGE_Keys *keys, 1230 const struct Account *a) 1231 { 1232 struct TALER_NormalizedPayto np; 1233 bool ret; 1234 1235 np = TALER_payto_normalize (a->merchant_account_uri); 1236 ret = TALER_EXCHANGE_keys_test_account_allowed (keys, 1237 true, 1238 np); 1239 GNUNET_free (np.normalized_payto); 1240 return ret; 1241 } 1242 1243 1244 /** 1245 * Start the KYC checking for account @a at exchange @a e. 1246 * 1247 * @param e an exchange 1248 * @param a an account 1249 */ 1250 static void 1251 start_inquiry (struct Exchange *e, 1252 struct Account *a) 1253 { 1254 struct Inquiry *i; 1255 enum GNUNET_DB_QueryStatus qs; 1256 1257 i = GNUNET_new (struct Inquiry); 1258 i->e = e; 1259 i->a = a; 1260 GNUNET_CONTAINER_DLL_insert (a->i_head, 1261 a->i_tail, 1262 i); 1263 qs = TALER_MERCHANTDB_set_instance (pg, 1264 a->instance_id); 1265 if (qs < 0) 1266 { 1267 GNUNET_break (0); 1268 global_ret = EXIT_FAILURE; 1269 GNUNET_SCHEDULER_shutdown (); 1270 return; 1271 } 1272 qs = TALER_MERCHANTDB_get_kyc_status (pg, 1273 a->merchant_account_uri, 1274 a->instance_id, 1275 e->keys->exchange_url, 1276 &i->auth_ok, 1277 &i->access_token, 1278 &i->kyc_ok, 1279 &i->last_http_status, 1280 &i->last_ec, 1281 &i->rule_gen, 1282 &i->last_kyc_check, 1283 &i->due, 1284 &i->backoff, 1285 &i->aml_review, 1286 &i->jlimits); 1287 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1288 "account_kyc_get_status (%s, %s, %s) returned %d (%u, #%llu)\n", 1289 i->a->instance_id, 1290 e->keys->exchange_url, 1291 a->merchant_account_uri.full_payto, 1292 (int) qs, 1293 i->last_http_status, 1294 (unsigned long long) i->rule_gen); 1295 if (qs < 0) 1296 { 1297 GNUNET_break (0); 1298 global_ret = EXIT_FAILURE; 1299 GNUNET_SCHEDULER_shutdown (); 1300 return; 1301 } 1302 if (qs > 0) 1303 i->not_first_time = true; 1304 if (GNUNET_YES == test_mode) 1305 i->due = GNUNET_TIME_UNIT_ZERO_ABS; /* immediately */ 1306 inquiry_work (i); 1307 } 1308 1309 1310 /** 1311 * Stop KYC inquiry @a i. 1312 * 1313 * @param[in] i the inquiry to stop 1314 */ 1315 static void 1316 stop_inquiry (struct Inquiry *i) 1317 { 1318 struct Account *a = i->a; 1319 1320 GNUNET_CONTAINER_DLL_remove (a->i_head, 1321 a->i_tail, 1322 i); 1323 if (NULL != i->task) 1324 { 1325 GNUNET_SCHEDULER_cancel (i->task); 1326 i->task = NULL; 1327 } 1328 if (NULL != i->kyc) 1329 { 1330 TALER_EXCHANGE_get_kyc_check_cancel (i->kyc); 1331 i->kyc = NULL; 1332 } 1333 if (NULL != i->kyc_info) 1334 { 1335 TALER_EXCHANGE_get_kyc_info_cancel (i->kyc_info); 1336 i->kyc_info = NULL; 1337 } 1338 if (NULL != i->tos_upload) 1339 { 1340 TALER_EXCHANGE_post_kyc_upload_cancel (i->tos_upload); 1341 i->tos_upload = NULL; 1342 } 1343 GNUNET_free (i->tos_etag); 1344 if (NULL != i->jlimits) 1345 { 1346 json_decref (i->jlimits); 1347 i->jlimits = NULL; 1348 } 1349 GNUNET_free (i); 1350 } 1351 1352 1353 /** 1354 * Stop KYC inquiry for account @a at exchange @a e. 1355 * 1356 * @param e an exchange 1357 * @param a an account 1358 */ 1359 static void 1360 stop_inquiry_at (struct Exchange *e, 1361 struct Account *a) 1362 { 1363 for (struct Inquiry *i = a->i_head; 1364 NULL != i; 1365 i = i->next) 1366 { 1367 if (e == i->e) 1368 { 1369 stop_inquiry (i); 1370 return; 1371 } 1372 } 1373 /* strange, there should have been a match! */ 1374 GNUNET_break (0); 1375 } 1376 1377 1378 /** 1379 * Set the account @a h_wire of @a instance_id to be ineligible 1380 * for the exchange at @a exchange_url and thus no need to do KYC checks. 1381 * 1382 * @param instance_id instance that has the account 1383 * @param exchange_url base URL of the exchange 1384 * @param h_wire hash of the merchant bank account that is ineligible 1385 */ 1386 static void 1387 flag_ineligible (const char *instance_id, 1388 const char *exchange_url, 1389 const struct TALER_MerchantWireHashP *h_wire) 1390 { 1391 enum GNUNET_DB_QueryStatus qs; 1392 1393 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1394 "Account %s not eligible at exchange %s\n", 1395 TALER_B2S (h_wire), 1396 exchange_url); 1397 qs = TALER_MERCHANTDB_set_instance (pg, 1398 instance_id); 1399 if (qs < 0) 1400 { 1401 GNUNET_break (0); 1402 global_ret = EXIT_FAILURE; 1403 GNUNET_SCHEDULER_shutdown (); 1404 return; 1405 } 1406 qs = TALER_MERCHANTDB_account_kyc_set_status ( 1407 pg, 1408 instance_id, 1409 h_wire, 1410 exchange_url, 1411 GNUNET_TIME_timestamp_get (), 1412 GNUNET_TIME_UNIT_FOREVER_ABS, 1413 GNUNET_TIME_UNIT_FOREVER_REL, 1414 0, 1415 TALER_EC_MERCHANT_PRIVATE_ACCOUNT_NOT_ELIGIBLE_FOR_EXCHANGE, 1416 0, 1417 NULL, 1418 NULL, 1419 false, 1420 false); 1421 GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 1422 TALER_MERCHANTDB_set_instance ( 1423 pg, 1424 NULL)); 1425 if (qs < 0) 1426 { 1427 GNUNET_break (0); 1428 global_ret = EXIT_FAILURE; 1429 GNUNET_SCHEDULER_shutdown (); 1430 return; 1431 } 1432 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1433 "account_kyc_set_status (%s) returned %d\n", 1434 exchange_url, 1435 (int) qs); 1436 } 1437 1438 1439 /** 1440 * Start inquries for all exchanges on account @a a. 1441 * 1442 * @param a an account 1443 */ 1444 static void 1445 start_inquiries (struct Account *a) 1446 { 1447 for (struct Exchange *e = e_head; 1448 NULL != e; 1449 e = e->next) 1450 { 1451 if (is_eligible (e->keys, 1452 a)) 1453 { 1454 start_inquiry (e, 1455 a); 1456 } 1457 else 1458 { 1459 flag_ineligible (a->instance_id, 1460 e->keys->exchange_url, 1461 &a->h_wire); 1462 } 1463 } 1464 } 1465 1466 1467 /** 1468 * Stop all inquries involving account @a a. 1469 * 1470 * @param a an account 1471 */ 1472 static void 1473 stop_inquiries (struct Account *a) 1474 { 1475 struct Inquiry *i; 1476 1477 while (NULL != (i = a->i_head)) 1478 stop_inquiry (i); 1479 } 1480 1481 1482 /** 1483 * Callback invoked with information about a bank account. 1484 * 1485 * @param cls closure 1486 * @param merchant_priv private key of the merchant instance 1487 * @param ad details about the account 1488 */ 1489 static void 1490 account_cb ( 1491 void *cls, 1492 const struct TALER_MerchantPrivateKeyP *merchant_priv, 1493 const struct TALER_MERCHANTDB_AccountDetails *ad) 1494 { 1495 struct TALER_FullPayto payto_uri = ad->payto_uri; 1496 1497 if (! ad->active) 1498 return; 1499 if (NULL == merchant_priv) 1500 return; /* instance was deleted */ 1501 for (struct Account *a = a_head; 1502 NULL != a; 1503 a = a->next) 1504 { 1505 if ( (0 == 1506 TALER_full_payto_cmp (payto_uri, 1507 a->merchant_account_uri)) && 1508 (0 == 1509 GNUNET_memcmp (&a->h_wire, 1510 &ad->h_wire)) && 1511 (0 == 1512 strcmp (ad->instance_id, 1513 a->instance_id)) ) 1514 { 1515 a->account_gen = database_gen; 1516 return; 1517 } 1518 } 1519 { 1520 struct Account *a = GNUNET_new (struct Account); 1521 1522 a->account_gen = database_gen; 1523 a->merchant_account_uri.full_payto 1524 = GNUNET_strdup (ad->payto_uri.full_payto); 1525 a->instance_id 1526 = GNUNET_strdup (ad->instance_id); 1527 a->h_wire 1528 = ad->h_wire; 1529 a->ap.merchant_priv 1530 = *merchant_priv; 1531 TALER_full_payto_normalize_and_hash (a->merchant_account_uri, 1532 &a->h_payto); 1533 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1534 "Found account %s of instance %s with H_PAYTO %s\n", 1535 ad->payto_uri.full_payto, 1536 ad->instance_id, 1537 GNUNET_sh2s (&a->h_payto.hash)); 1538 GNUNET_CONTAINER_DLL_insert (a_head, 1539 a_tail, 1540 a); 1541 start_inquiries (a); 1542 } 1543 } 1544 1545 1546 /** 1547 * The set of bank accounts has changed, update our 1548 * list of active inquiries. 1549 * 1550 * @param cls unused 1551 */ 1552 static void 1553 find_accounts (void *cls) 1554 { 1555 enum GNUNET_DB_QueryStatus qs; 1556 1557 (void) cls; 1558 account_task = NULL; 1559 database_gen++; 1560 qs = TALER_MERCHANTDB_select_accounts (pg, 1561 &account_cb, 1562 NULL); 1563 if (qs < 0) 1564 { 1565 GNUNET_break (0); 1566 global_ret = EXIT_FAILURE; 1567 GNUNET_SCHEDULER_shutdown (); 1568 return; 1569 } 1570 for (struct Account *a = a_head; 1571 NULL != a; 1572 a = a->next) 1573 { 1574 if (a->account_gen < database_gen) 1575 stop_inquiries (a); 1576 } 1577 if ( (! at_limit) && 1578 (0 == active_inquiries) && 1579 (test_mode) ) 1580 { 1581 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1582 "No more open inquiries and in test mode. Existing.\n"); 1583 GNUNET_SCHEDULER_shutdown (); 1584 return; 1585 } 1586 } 1587 1588 1589 /** 1590 * Function called when transfers are added to the merchant database. We look 1591 * for more work. 1592 * 1593 * @param cls closure (NULL) 1594 * @param extra additional event data provided 1595 * @param extra_size number of bytes in @a extra 1596 */ 1597 static void 1598 account_changed (void *cls, 1599 const void *extra, 1600 size_t extra_size) 1601 { 1602 (void) cls; 1603 (void) extra; 1604 (void) extra_size; 1605 if (NULL != account_task) 1606 return; 1607 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1608 "Received account change notification: reloading accounts\n"); 1609 account_task 1610 = GNUNET_SCHEDULER_add_now (&find_accounts, 1611 NULL); 1612 } 1613 1614 1615 /** 1616 * Interact with the database to get the current set 1617 * of exchange keys known to us. 1618 * 1619 * @param exchange_url the exchange URL to check 1620 */ 1621 static void 1622 find_keys (const char *exchange_url) 1623 { 1624 enum GNUNET_DB_QueryStatus qs; 1625 struct TALER_EXCHANGE_Keys *keys; 1626 struct Exchange *e; 1627 struct GNUNET_TIME_Absolute first_retry; 1628 1629 qs = TALER_MERCHANTDB_select_exchange_keys (pg, 1630 exchange_url, 1631 &first_retry, 1632 &keys); 1633 if (qs < 0) 1634 { 1635 GNUNET_break (0); 1636 global_ret = EXIT_FAILURE; 1637 GNUNET_SCHEDULER_shutdown (); 1638 return; 1639 } 1640 if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) || 1641 (NULL == keys) ) 1642 { 1643 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1644 "No %s/keys yet!\n", 1645 exchange_url); 1646 return; 1647 } 1648 for (e = e_head; NULL != e; e = e->next) 1649 { 1650 if (0 == strcmp (e->keys->exchange_url, 1651 keys->exchange_url)) 1652 { 1653 struct TALER_EXCHANGE_Keys *old_keys = e->keys; 1654 1655 e->keys = keys; 1656 for (struct Account *a = a_head; 1657 NULL != a; 1658 a = a->next) 1659 { 1660 bool was_eligible = is_eligible (old_keys, 1661 a); 1662 bool now_eligible = is_eligible (keys, 1663 a); 1664 1665 if (was_eligible == now_eligible) 1666 continue; /* no change, do nothing */ 1667 if (was_eligible) 1668 stop_inquiry_at (e, 1669 a); 1670 else /* is_eligible */ 1671 start_inquiry (e, 1672 a); 1673 } 1674 TALER_EXCHANGE_keys_decref (old_keys); 1675 return; 1676 } 1677 } 1678 e = GNUNET_new (struct Exchange); 1679 e->keys = keys; 1680 GNUNET_CONTAINER_DLL_insert (e_head, 1681 e_tail, 1682 e); 1683 for (struct Account *a = a_head; 1684 NULL != a; 1685 a = a->next) 1686 { 1687 if ( (a->account_gen == database_gen) && 1688 (is_eligible (e->keys, 1689 a)) ) 1690 start_inquiry (e, 1691 a); 1692 } 1693 } 1694 1695 1696 /** 1697 * Function called when keys were changed in the 1698 * merchant database. Updates ours. 1699 * 1700 * @param cls closure (NULL) 1701 * @param extra additional event data provided 1702 * @param extra_size number of bytes in @a extra 1703 */ 1704 static void 1705 keys_changed (void *cls, 1706 const void *extra, 1707 size_t extra_size) 1708 { 1709 const char *url = extra; 1710 1711 (void) cls; 1712 if ( (NULL == extra) || 1713 (0 == extra_size) ) 1714 { 1715 GNUNET_break (0); 1716 global_ret = EXIT_FAILURE; 1717 GNUNET_SCHEDULER_shutdown (); 1718 return; 1719 } 1720 if ('\0' != url[extra_size - 1]) 1721 { 1722 GNUNET_break (0); 1723 global_ret = EXIT_FAILURE; 1724 GNUNET_SCHEDULER_shutdown (); 1725 return; 1726 } 1727 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1728 "Received keys change notification: reload `%s'\n", 1729 url); 1730 find_keys (url); 1731 } 1732 1733 1734 /** 1735 * Function called when a KYC rule was triggered by 1736 * a transaction and we need to get the latest KYC 1737 * status immediately. 1738 * 1739 * @param cls closure (NULL) 1740 * @param extra additional event data provided 1741 * @param extra_size number of bytes in @a extra 1742 */ 1743 static void 1744 rule_triggered (void *cls, 1745 const void *extra, 1746 size_t extra_size) 1747 { 1748 const char *text = extra; 1749 const char *space; 1750 struct TALER_MerchantWireHashP h_wire; 1751 const char *exchange_url; 1752 1753 (void) cls; 1754 if ( (NULL == extra) || 1755 (0 == extra_size) ) 1756 { 1757 GNUNET_break (0); 1758 global_ret = EXIT_FAILURE; 1759 GNUNET_SCHEDULER_shutdown (); 1760 return; 1761 } 1762 if ('\0' != text[extra_size - 1]) 1763 { 1764 GNUNET_break (0); 1765 global_ret = EXIT_FAILURE; 1766 GNUNET_SCHEDULER_shutdown (); 1767 return; 1768 } 1769 space = memchr (extra, 1770 ' ', 1771 extra_size); 1772 if (NULL == space) 1773 { 1774 GNUNET_break (0); 1775 global_ret = EXIT_FAILURE; 1776 GNUNET_SCHEDULER_shutdown (); 1777 return; 1778 } 1779 if (GNUNET_OK != 1780 GNUNET_STRINGS_string_to_data (extra, 1781 space - text, 1782 &h_wire, 1783 sizeof (h_wire))) 1784 { 1785 GNUNET_break (0); 1786 global_ret = EXIT_FAILURE; 1787 GNUNET_SCHEDULER_shutdown (); 1788 return; 1789 } 1790 exchange_url = &space[1]; 1791 if (! TALER_is_web_url (exchange_url)) 1792 { 1793 GNUNET_break (0); 1794 global_ret = EXIT_FAILURE; 1795 GNUNET_SCHEDULER_shutdown (); 1796 return; 1797 } 1798 1799 for (struct Account *a = a_head; 1800 NULL != a; 1801 a = a->next) 1802 { 1803 if (0 != 1804 GNUNET_memcmp (&h_wire, 1805 &a->h_wire)) 1806 continue; 1807 for (struct Inquiry *i = a->i_head; 1808 NULL != i; 1809 i = i->next) 1810 { 1811 if (0 != strcmp (exchange_url, 1812 i->e->keys->exchange_url)) 1813 continue; 1814 i->kyc_ok = false; 1815 i->due = GNUNET_TIME_UNIT_ZERO_ABS; 1816 if (NULL != i->task) 1817 { 1818 GNUNET_SCHEDULER_cancel (i->task); 1819 i->task = NULL; 1820 } 1821 if (NULL != i->kyc) 1822 { 1823 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1824 "/kyc-check already running for %s\n", 1825 text); 1826 return; 1827 } 1828 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1829 "Starting %skyc-check for `%s' due to KYC rule trigger\n", 1830 exchange_url, 1831 i->a->merchant_account_uri.full_payto); 1832 i->task = GNUNET_SCHEDULER_add_at (i->due, 1833 &inquiry_work, 1834 i); 1835 return; 1836 } 1837 } 1838 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1839 "KYC rule trigger notification `%s' matches none of our accounts\n", 1840 text); 1841 } 1842 1843 1844 /** 1845 * Function called on each configuration section. Finds sections 1846 * about exchanges, parses the entries. 1847 * 1848 * @param cls NULL 1849 * @param section name of the section 1850 */ 1851 static void 1852 accept_exchanges (void *cls, 1853 const char *section) 1854 { 1855 char *url; 1856 1857 (void) cls; 1858 if (0 != 1859 strncasecmp (section, 1860 "merchant-exchange-", 1861 strlen ("merchant-exchange-"))) 1862 return; 1863 if (GNUNET_YES == 1864 GNUNET_CONFIGURATION_get_value_yesno (cfg, 1865 section, 1866 "DISABLED")) 1867 return; 1868 if (GNUNET_OK != 1869 GNUNET_CONFIGURATION_get_value_string (cfg, 1870 section, 1871 "EXCHANGE_BASE_URL", 1872 &url)) 1873 { 1874 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1875 section, 1876 "EXCHANGE_BASE_URL"); 1877 global_ret = EXIT_NOTCONFIGURED; 1878 GNUNET_SCHEDULER_shutdown (); 1879 return; 1880 } 1881 find_keys (url); 1882 GNUNET_free (url); 1883 } 1884 1885 1886 /** 1887 * We're being aborted with CTRL-C (or SIGTERM). Shut down. 1888 * 1889 * @param cls closure (NULL) 1890 */ 1891 static void 1892 shutdown_task (void *cls) 1893 { 1894 (void) cls; 1895 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1896 "Running shutdown\n"); 1897 while (NULL != e_head) 1898 { 1899 struct Exchange *e = e_head; 1900 1901 if (NULL != e->keys) 1902 { 1903 TALER_EXCHANGE_keys_decref (e->keys); 1904 e->keys = NULL; 1905 } 1906 GNUNET_CONTAINER_DLL_remove (e_head, 1907 e_tail, 1908 e); 1909 GNUNET_free (e); 1910 } 1911 while (NULL != a_head) 1912 { 1913 struct Account *a = a_head; 1914 1915 stop_inquiries (a); 1916 GNUNET_CONTAINER_DLL_remove (a_head, 1917 a_tail, 1918 a); 1919 GNUNET_free (a->merchant_account_uri.full_payto); 1920 GNUNET_free (a->instance_id); 1921 GNUNET_free (a); 1922 } 1923 if (NULL != eh_accounts) 1924 { 1925 TALER_MERCHANTDB_event_listen_cancel (eh_accounts); 1926 eh_accounts = NULL; 1927 } 1928 if (NULL != account_task) 1929 { 1930 GNUNET_SCHEDULER_cancel (account_task); 1931 account_task = NULL; 1932 } 1933 if (NULL != eh_keys) 1934 { 1935 TALER_MERCHANTDB_event_listen_cancel (eh_keys); 1936 eh_keys = NULL; 1937 } 1938 if (NULL != eh_rule) 1939 { 1940 TALER_MERCHANTDB_event_listen_cancel (eh_rule); 1941 eh_rule = NULL; 1942 } 1943 if (NULL != eh_update_forced) 1944 { 1945 TALER_MERCHANTDB_event_listen_cancel (eh_update_forced); 1946 eh_update_forced = NULL; 1947 } 1948 if (NULL != keys_rule) 1949 { 1950 TALER_MERCHANTDB_event_listen_cancel (keys_rule); 1951 keys_rule = NULL; 1952 } 1953 if (NULL != pg) 1954 { 1955 TALER_MERCHANTDB_disconnect (pg); 1956 pg = NULL; 1957 } 1958 cfg = NULL; 1959 if (NULL != ctx) 1960 { 1961 GNUNET_CURL_fini (ctx); 1962 ctx = NULL; 1963 } 1964 if (NULL != rc) 1965 { 1966 GNUNET_CURL_gnunet_rc_destroy (rc); 1967 rc = NULL; 1968 } 1969 } 1970 1971 1972 /** 1973 * Function called when we urgently need to re-check the KYC status 1974 * of some account. Finds the respective inquiry and re-launches 1975 * the check, unless we are already doing it. 1976 * 1977 * @param cls NULL 1978 * @param instance_id instance for which to force the check 1979 * @param exchange_url base URL of the exchange to check 1980 * @param h_wire hash of the wire account to check KYC status for 1981 */ 1982 static void 1983 force_check_now (void *cls, 1984 const char *instance_id, 1985 const char *exchange_url, 1986 const struct TALER_MerchantWireHashP *h_wire) 1987 { 1988 for (struct Account *a = a_head; 1989 NULL != a; 1990 a = a->next) 1991 { 1992 if (0 != 1993 strcmp (instance_id, 1994 a->instance_id)) 1995 continue; 1996 if (0 != 1997 GNUNET_memcmp (h_wire, 1998 &a->h_wire)) 1999 continue; 2000 for (struct Inquiry *i = a->i_head; 2001 NULL != i; 2002 i = i->next) 2003 { 2004 if (0 != 2005 strcmp (i->e->keys->exchange_url, 2006 exchange_url)) 2007 continue; 2008 /* If we are not actively checking with the exchange, do start 2009 to check immediately */ 2010 if (NULL != i->kyc) 2011 { 2012 i->due = GNUNET_TIME_absolute_get (); /* now! */ 2013 if (NULL != i->task) 2014 GNUNET_SCHEDULER_cancel (i->task); 2015 i->task = GNUNET_SCHEDULER_add_at (i->due, 2016 &inquiry_work, 2017 i); 2018 } 2019 return; 2020 } 2021 } 2022 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2023 "No inquiry at `%s' for exchange `%s' and h_wire `%s'. Likely the account is not eligible.\n", 2024 instance_id, 2025 exchange_url, 2026 TALER_B2S (h_wire)); 2027 /* In this case, set the due date back to FOREVER */ 2028 flag_ineligible (instance_id, 2029 exchange_url, 2030 h_wire); 2031 } 2032 2033 2034 /** 2035 * Function called when a KYC status update was forced by an 2036 * application checking the KYC status of an account. 2037 * 2038 * @param cls closure (NULL) 2039 * @param extra additional event data provided 2040 * @param extra_size number of bytes in @a extra 2041 */ 2042 static void 2043 update_forced (void *cls, 2044 const void *extra, 2045 size_t extra_size) 2046 { 2047 enum GNUNET_DB_QueryStatus qs; 2048 2049 (void) cls; 2050 (void) extra; 2051 (void) extra_size; 2052 qs = TALER_MERCHANTDB_account_kyc_get_outdated ( 2053 pg, 2054 &force_check_now, 2055 NULL); 2056 if (qs < 0) 2057 { 2058 GNUNET_break (0); 2059 global_ret = EXIT_FAILURE; 2060 GNUNET_SCHEDULER_shutdown (); 2061 return; 2062 } 2063 } 2064 2065 2066 /** 2067 * First task. 2068 * 2069 * @param cls closure, NULL 2070 * @param args remaining command-line arguments 2071 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 2072 * @param c configuration 2073 */ 2074 static void 2075 run (void *cls, 2076 char *const *args, 2077 const char *cfgfile, 2078 const struct GNUNET_CONFIGURATION_Handle *c) 2079 { 2080 (void) args; 2081 (void) cfgfile; 2082 2083 cfg = c; 2084 if (GNUNET_OK != 2085 GNUNET_CONFIGURATION_get_value_time (cfg, 2086 "merchant-kyccheck", 2087 "AML_FREQ", 2088 &aml_freq)) 2089 { 2090 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 2091 "merchant-kyccheck", 2092 "AML_FREQ"); 2093 /* use default */ 2094 aml_freq = AML_FREQ; 2095 } 2096 if (GNUNET_OK != 2097 GNUNET_CONFIGURATION_get_value_time (cfg, 2098 "merchant-kyccheck", 2099 "AML_LOW_FREQ", 2100 &aml_low_freq)) 2101 { 2102 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 2103 "merchant-kyccheck", 2104 "AML_LOW_FREQ"); 2105 /* use default */ 2106 aml_low_freq = AML_LOW_FREQ; 2107 } 2108 if (GNUNET_TIME_relative_cmp (aml_low_freq, 2109 <, 2110 aml_freq)) 2111 { 2112 aml_low_freq = GNUNET_TIME_relative_multiply (aml_freq, 2113 10); 2114 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2115 "AML_LOW_FREQ was set to less than AML_FREQ. Using %s instead\n", 2116 GNUNET_TIME_relative2s (aml_low_freq, 2117 true)); 2118 } 2119 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, 2120 NULL); 2121 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 2122 &rc); 2123 rc = GNUNET_CURL_gnunet_rc_create (ctx); 2124 if (NULL == ctx) 2125 { 2126 GNUNET_break (0); 2127 GNUNET_SCHEDULER_shutdown (); 2128 global_ret = EXIT_FAILURE; 2129 return; 2130 } 2131 if (NULL == 2132 (pg = TALER_MERCHANTDB_connect (cfg))) 2133 { 2134 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2135 "Failed to initialize DB subsystem. Consider running taler-merchant-dbconfig.\n"); 2136 GNUNET_SCHEDULER_shutdown (); 2137 global_ret = EXIT_FAILURE; 2138 return; 2139 } 2140 { 2141 struct GNUNET_DB_EventHeaderP es = { 2142 .size = htons (sizeof (es)), 2143 .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS) 2144 }; 2145 2146 eh_keys 2147 = TALER_MERCHANTDB_event_listen (pg, 2148 &es, 2149 GNUNET_TIME_UNIT_FOREVER_REL, 2150 &keys_changed, 2151 NULL); 2152 } 2153 { 2154 struct GNUNET_DB_EventHeaderP es = { 2155 .size = htons (sizeof (es)), 2156 .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_UPDATE_FORCED) 2157 }; 2158 2159 eh_update_forced 2160 = TALER_MERCHANTDB_event_listen (pg, 2161 &es, 2162 GNUNET_TIME_UNIT_FOREVER_REL, 2163 &update_forced, 2164 NULL); 2165 } 2166 { 2167 struct GNUNET_DB_EventHeaderP es = { 2168 .size = htons (sizeof (es)), 2169 .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_RULE_TRIGGERED) 2170 }; 2171 2172 eh_rule 2173 = TALER_MERCHANTDB_event_listen (pg, 2174 &es, 2175 GNUNET_TIME_UNIT_FOREVER_REL, 2176 &rule_triggered, 2177 NULL); 2178 } 2179 GNUNET_CONFIGURATION_iterate_sections (cfg, 2180 &accept_exchanges, 2181 NULL); 2182 { 2183 struct GNUNET_DB_EventHeaderP es = { 2184 .size = htons (sizeof (es)), 2185 .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED) 2186 }; 2187 2188 eh_accounts 2189 = TALER_MERCHANTDB_event_listen (pg, 2190 &es, 2191 GNUNET_TIME_UNIT_FOREVER_REL, 2192 &account_changed, 2193 NULL); 2194 } 2195 GNUNET_assert (NULL == account_task); 2196 account_task 2197 = GNUNET_SCHEDULER_add_now (&find_accounts, 2198 NULL); 2199 } 2200 2201 2202 /** 2203 * The main function of taler-merchant-kyccheck 2204 * 2205 * @param argc number of arguments from the command line 2206 * @param argv command line arguments 2207 * @return 0 ok, 1 on error 2208 */ 2209 int 2210 main (int argc, 2211 char *const *argv) 2212 { 2213 struct GNUNET_GETOPT_CommandLineOption options[] = { 2214 GNUNET_GETOPT_option_timetravel ('T', 2215 "timetravel"), 2216 GNUNET_GETOPT_option_flag ('t', 2217 "test", 2218 "run in test mode and exit when idle", 2219 &test_mode), 2220 GNUNET_GETOPT_option_version (VERSION), 2221 GNUNET_GETOPT_OPTION_END 2222 }; 2223 enum GNUNET_GenericReturnValue ret; 2224 2225 ret = GNUNET_PROGRAM_run ( 2226 TALER_MERCHANT_project_data (), 2227 argc, argv, 2228 "taler-merchant-kyccheck", 2229 gettext_noop ( 2230 "background process that checks the KYC state of our bank accounts at various exchanges"), 2231 options, 2232 &run, NULL); 2233 if (GNUNET_SYSERR == ret) 2234 return EXIT_INVALIDARGUMENT; 2235 if (GNUNET_NO == ret) 2236 return EXIT_SUCCESS; 2237 return global_ret; 2238 } 2239 2240 2241 /* end of taler-merchant-kyccheck.c */