exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 127296c7dd4df1ed2752eda297af2e12a10b97e4
parent 8823084f2ae0f3d5b40795c91a6714d0259cd5e3
Author: Christian Grothoff <christian@grothoff.org>
Date:   Thu, 16 May 2024 16:47:58 +0200

work all over the place

Diffstat:
Msrc/exchange/taler-exchange-aggregator.c | 3+--
Msrc/exchange/taler-exchange-httpd.c | 2+-
Msrc/exchange/taler-exchange-httpd.h | 18++++++------------
Msrc/exchange/taler-exchange-httpd_kyc-check.c | 476++++++++++++++-----------------------------------------------------------------
Msrc/exchange/taler-exchange-httpd_kyc-wallet.c | 6+-----
Msrc/exchange/taler-exchange-httpd_purses_merge.c | 4----
Msrc/exchange/taler-exchange-httpd_reserves_close.c | 6------
Msrc/exchange/taler-exchange-httpd_reserves_purse.c | 4----
Msrc/exchange/taler-exchange-httpd_withdraw.c | 15+++++----------
Msrc/exchange/taler-exchange-httpd_withdraw.h | 2--
Msrc/exchangedb/0005-legitimization_measures.sql | 4+---
Msrc/exchangedb/pg_lookup_kyc_requirement_by_row.c | 31++++++++++++++++++-------------
Msrc/exchangedb/pg_lookup_kyc_requirement_by_row.h | 12++++++------
Msrc/include/taler_crypto_lib.h | 48++++++++++++++++++++++++++++++++++++------------
Msrc/include/taler_exchangedb_plugin.h | 18+++++++-----------
Msrc/include/taler_kyclogic_lib.h | 41+++++++++++++++++++++++++++++++++++++----
Msrc/kyclogic/kyclogic_api.c | 27++++++++++++++++++++++++++-
17 files changed, 227 insertions(+), 490 deletions(-)

diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c @@ -550,11 +550,10 @@ legitimization_satisfied (struct AggregationUnit *au_active) "KYC requirement for %s is %s\n", TALER_amount2s (&au_active->total_amount), TALER_KYCLOGIC_rule2s (requirement)); - jrule = TALER_KYCLOGIC_rule2j (requirement); + jrule = TALER_KYCLOGIC_rule_to_measures (requirement); qs = db_plugin->trigger_kyc_rule_for_account ( db_plugin->cls, &au_active->h_payto, - NULL, /* FIXME: get account pub? Or is NULL fine? */ jrule, TALER_KYCLOGIC_rule2priority (requirement), &au_active->requirement_row); diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA + Copyright (C) 2014-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h @@ -42,6 +42,12 @@ extern struct GNUNET_TIME_Relative TEH_max_keys_caching; extern struct GNUNET_TIME_Relative TEH_reserve_closing_delay; /** + * Name of the KYC-AML-trigger evaluation binary. + * FIXME: do we keep this? + */ +extern char *TEH_kyc_aml_trigger; + +/** * The exchange's configuration. */ extern const struct GNUNET_CONFIGURATION_Handle *TEH_cfg; @@ -123,18 +129,6 @@ extern const struct TALER_CurrencySpecification *TEH_cspec; extern char *TEH_currency; /** - * Name of the KYC-AML-trigger evaluation binary. - */ -extern char *TEH_kyc_aml_trigger; - -/** - * What is the largest amount we allow a peer to - * merge into a reserve before always triggering - * an AML check? - */ -extern struct TALER_Amount TEH_aml_threshold; - -/** * Our (externally visible) base URL. */ extern char *TEH_base_url; diff --git a/src/exchange/taler-exchange-httpd_kyc-check.c b/src/exchange/taler-exchange-httpd_kyc-check.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021-2023 Taler Systems SA + Copyright (C) 2021-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -55,17 +55,6 @@ struct KycPoller struct MHD_Connection *connection; /** - * Logic for @e ih - */ - struct TALER_KYCLOGIC_Plugin *ih_logic; - - /** - * Handle to asynchronously running KYC initiation - * request. - */ - struct TALER_KYCLOGIC_InitiateHandle *ih; - - /** * Subscription for the database event we are * waiting for. */ @@ -77,61 +66,21 @@ struct KycPoller uint64_t requirement_row; /** - * Row of KYC process being initiated. - */ - uint64_t process_row; - - /** - * Hash of the payto:// URI we are confirming to - * have finished the KYC for. - */ - struct TALER_PaytoHashP h_payto; - - /** * When will this request time out? */ struct GNUNET_TIME_Absolute timeout; /** - * If the KYC complete, what kind of data was collected? - */ - json_t *kyc_details; - - /** - * Set to starting URL of KYC process if KYC is required. - */ - char *kyc_url; - - /** - * Set to error details, on error (@ec not TALER_EC_NONE). + * Signature by the account owner authorizing this + * operation. */ - char *hint; - - /** - * Name of the section of the provider in the configuration. - */ - const char *section_name; - - /** - * Set to error encountered with KYC logic, if any. - */ - enum TALER_ErrorCode ec; + union TALER_AccountSignatureP account_sig; /** * True if we are still suspended. */ bool suspended; - /** - * False if KYC is not required. - */ - bool kyc_required; - - /** - * True if we once tried the KYC initiation. - */ - bool ih_done; - }; @@ -156,11 +105,6 @@ TEH_kyc_check_cleanup () GNUNET_CONTAINER_DLL_remove (kyp_head, kyp_tail, kyp); - if (NULL != kyp->ih) - { - kyp->ih_logic->initiate_cancel (kyp->ih); - kyp->ih = NULL; - } if (kyp->suspended) { kyp->suspended = false; @@ -190,261 +134,10 @@ kyp_cleanup (struct TEH_RequestContext *rc) kyp->eh); kyp->eh = NULL; } - if (NULL != kyp->ih) - { - kyp->ih_logic->initiate_cancel (kyp->ih); - kyp->ih = NULL; - } - json_decref (kyp->kyc_details); - GNUNET_free (kyp->kyc_url); - GNUNET_free (kyp->hint); GNUNET_free (kyp); } -#if FIXME -/** - * Function called with the result of a KYC initiation - * operation. - * - * @param cls closure with our `struct KycPoller *` - * @param ec #TALER_EC_NONE on success - * @param redirect_url set to where to redirect the user on success, NULL on failure - * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown - * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown - * @param error_msg_hint set to additional details to return to user, NULL on success - */ -static void -initiate_cb ( - void *cls, - enum TALER_ErrorCode ec, - const char *redirect_url, - const char *provider_user_id, - const char *provider_legitimization_id, - const char *error_msg_hint) -{ - struct KycPoller *kyp = cls; - enum GNUNET_DB_QueryStatus qs; - - kyp->ih = NULL; - kyp->ih_done = true; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "KYC initiation `%s' completed with ec=%d (%s)\n", - provider_legitimization_id, - ec, - (TALER_EC_NONE == ec) - ? redirect_url - : error_msg_hint); - kyp->ec = ec; - if (TALER_EC_NONE == ec) - { - kyp->kyc_url = GNUNET_strdup (redirect_url); - } - else - { - kyp->hint = GNUNET_strdup (error_msg_hint); - } - qs = TEH_plugin->update_kyc_process_by_row ( - TEH_plugin->cls, - kyp->process_row, - kyp->section_name, - &kyp->h_payto, - provider_user_id, - provider_legitimization_id, - redirect_url, - GNUNET_TIME_UNIT_ZERO_ABS); - if (qs <= 0) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "KYC requirement update failed for %s with status %d at %s:%u\n", - TALER_B2S (&kyp->h_payto), - qs, - __FILE__, - __LINE__); - GNUNET_assert (kyp->suspended); - kyp->suspended = false; - GNUNET_CONTAINER_DLL_remove (kyp_head, - kyp_tail, - kyp); - MHD_resume_connection (kyp->connection); - TALER_MHD_daemon_trigger (); -} - - -#endif - - -/** - * Function implementing database transaction to check wallet's KYC status. - * Runs the transaction logic; IF it returns a non-error code, the transaction - * logic MUST NOT queue a MHD response. IF it returns an hard error, the - * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it - * returns the soft error code, the function MAY be called again to retry and - * MUST not queue a MHD response. - * - * @param cls closure with a `struct KycPoller *` - * @param connection MHD request which triggered the transaction - * @param[out] mhd_ret set to MHD response status for @a connection, - * if transaction failed (!) - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -kyc_check (void *cls, - struct MHD_Connection *connection, - MHD_RESULT *mhd_ret) -{ -#if FIXME - struct KycPoller *kyp = cls; - enum GNUNET_DB_QueryStatus qs; - struct TALER_KYCLOGIC_ProviderDetails *pd; - enum GNUNET_GenericReturnValue ret; - struct TALER_PaytoHashP h_payto; - char *requirements; - char *redirect_url; - bool satisfied; - - qs = TEH_plugin->lookup_kyc_requirement_by_row ( - TEH_plugin->cls, - kyp->requirement_row, - &requirements, - &h_payto); - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No KYC requirements open for %llu\n", - (unsigned long long) kyp->requirement_row); - return qs; - } - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); - return qs; - } - if (0 != - GNUNET_memcmp (&kyp->h_payto, - &h_payto)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Requirement %llu provided, but h_payto does not match\n", - (unsigned long long) kyp->requirement_row); - GNUNET_break_op (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_KYC_CHECK_AUTHORIZATION_FAILED, - "h_payto"); - GNUNET_free (requirements); - return GNUNET_DB_STATUS_HARD_ERROR; - } - qs = TALER_KYCLOGIC_check_satisfied ( - &requirements, - &h_payto, - &kyp->kyc_details, - TEH_plugin->select_satisfied_kyc_processes, - TEH_plugin->cls, - &satisfied); - if (qs < 0) - { - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - return qs; - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "kyc_test_required"); - GNUNET_free (requirements); - return GNUNET_DB_STATUS_HARD_ERROR; - } - if (satisfied) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "KYC requirements `%s' already satisfied\n", - requirements); - GNUNET_free (requirements); - return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; - } - - kyp->kyc_required = true; - ret = TALER_KYCLOGIC_requirements_to_logic (requirements, - &kyp->ih_logic, - &pd, - &kyp->section_name); - if (GNUNET_OK != ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "KYC requirements `%s' cannot be checked, but are set as required in database!\n", - requirements); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_GONE, - requirements); - GNUNET_free (requirements); - return GNUNET_DB_STATUS_HARD_ERROR; - } - GNUNET_free (requirements); - - if (kyp->ih_done) - return qs; - qs = TEH_plugin->get_pending_kyc_requirement_process ( - TEH_plugin->cls, - &h_payto, - kyp->section_name, - &redirect_url); - if (qs < 0) - { - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - return qs; - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_requirement_process"); - return GNUNET_DB_STATUS_HARD_ERROR; - } - if ( (qs > 0) && - (NULL != redirect_url) ) - { - kyp->kyc_url = redirect_url; - return qs; - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - /* set up new requirement process */ - qs = TEH_plugin->insert_kyc_requirement_process ( - TEH_plugin->cls, - &h_payto, - kyp->section_name, - NULL, - NULL, - &kyp->process_row); - if (qs < 0) - { - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - return qs; - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_requirement_process"); - return GNUNET_DB_STATUS_HARD_ERROR; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Initiating KYC check with logic %s\n", - kyp->ih_logic->name); - kyp->ih = kyp->ih_logic->initiate (kyp->ih_logic->cls, - pd, - &h_payto, - kyp->process_row, - &initiate_cb, - kyp); - GNUNET_break (NULL != kyp->ih); - return qs; -#else - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; -#endif -} - - /** * Function called on events received from Postgres. * Wakes up long pollers. @@ -491,12 +184,17 @@ TEH_handler_kyc_check ( const char *const args[1]) { struct KycPoller *kyp = rc->rh_ctx; - MHD_RESULT res; - enum GNUNET_GenericReturnValue ret; - struct GNUNET_TIME_Timestamp now; + json_t *jrules = NULL; + json_t *jlimits = NULL; + union TALER_AccountPublicKeyP account_pub; + struct TALER_AccountAccessTokenP access_token; + bool aml_review; + bool kyc_required; if (NULL == kyp) { + bool sig_required = true; + kyp = GNUNET_new (struct KycPoller); kyp->connection = rc->connection; rc->rh_ctx = kyp; @@ -513,26 +211,23 @@ TEH_handler_kyc_check ( &dummy)) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "requirement_row"); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "requirement_row"); } kyp->requirement_row = (uint64_t) requirement_row; } + TALER_MHD_parse_request_header_auto ( + rc->connection, + TALER_HTTP_HEADER_ACCOUNT_OWNER_SIGNATURE, + &kyp->account_sig, + sig_required); TALER_MHD_parse_request_timeout (rc->connection, &kyp->timeout); } - /* KYC plugin generated reply? */ - if (NULL != kyp->kyc_url) - { - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_ACCEPTED, - GNUNET_JSON_pack_string ("kyc_url", - kyp->kyc_url)); - } if ( (NULL == kyp->eh) && GNUNET_TIME_absolute_is_future (kyp->timeout) ) @@ -540,7 +235,7 @@ TEH_handler_kyc_check ( struct TALER_KycCompletedEventP rep = { .header.size = htons (sizeof (rep)), .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED), - .h_payto = kyp->h_payto + // .h_payto = kyp->h_payto // FIXME: h_payto not available here yet! }; GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -553,73 +248,70 @@ TEH_handler_kyc_check ( rc); } - now = GNUNET_TIME_timestamp_get (); - ret = TEH_DB_run_transaction (rc->connection, - "kyc check", - TEH_MT_REQUEST_OTHER, - &res, - &kyc_check, - kyp); - if (GNUNET_SYSERR == ret) + if (! TALER_KYCLOGIC_is_enabled ()) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Transaction failed.\n"); - return res; - } - /* KYC plugin generated reply? */ - if (NULL != kyp->kyc_url) - { - return TALER_MHD_REPLY_JSON_PACK ( + "KYC not enabled\n"); + return TALER_MHD_reply_static ( rc->connection, - MHD_HTTP_ACCEPTED, - GNUNET_JSON_pack_string ("kyc_url", - kyp->kyc_url)); + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); } - if ( (NULL == kyp->ih) && - (! kyp->kyc_required) ) { -#if FIXME - if (TALER_AML_NORMAL != kyp->aml_status) + enum GNUNET_DB_QueryStatus qs; + + qs = TEH_plugin->lookup_kyc_requirement_by_row ( + TEH_plugin->cls, + kyp->requirement_row, + &account_pub, + &access_token, + &jrules, + &aml_review, + &kyc_required); + if (qs < 0) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "KYC is OK, but AML active: %d\n", - (int) kyp->aml_status); - return TALER_MHD_REPLY_JSON_PACK ( + GNUNET_break (0); + return TALER_MHD_reply_with_ec ( rc->connection, - MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, - GNUNET_JSON_pack_uint64 ("aml_status", - kyp->aml_status)); + TALER_EC_GENERIC_DB_STORE_FAILED, + "lookup_kyc_requirement_by_row"); + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_KYC_CHECK_REQUEST_UNKNOWN, + NULL); } -#endif - /* KYC not required */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "KYC not required %llu\n", - (unsigned long long) kyp->requirement_row); - return TALER_MHD_reply_static ( - rc->connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); } - if (NULL != kyp->ih) + // FIXME: check signature! + + jlimits = TALER_KYCLOGIC_rules_to_limits (jrules); + if (NULL == jlimits) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Suspending HTTP request on KYC logic...\n"); - kyp->suspended = true; - GNUNET_CONTAINER_DLL_insert (kyp_head, - kyp_tail, - kyp); - MHD_suspend_connection (kyp->connection); - return MHD_YES; + GNUNET_break_op (0); + json_decref (jrules); + jrules = NULL; + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_INVARIANT_FAILURE, + "/kyc-check: rules_to_limits failed"); } + json_decref (jrules); + jrules = NULL; - /* long polling? */ - if ( (NULL != kyp->section_name) && - GNUNET_TIME_absolute_is_future (kyp->timeout)) + /* long polling for positive result? */ + if (kyc_required && + GNUNET_TIME_absolute_is_future (kyp->timeout)) { + json_decref (jlimits); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Suspending HTTP request on timeout (%s) now...\n", GNUNET_TIME_relative2s (GNUNET_TIME_absolute_get_remaining ( @@ -627,7 +319,6 @@ TEH_handler_kyc_check ( true)); GNUNET_assert (NULL != kyp->eh); kyp->suspended = true; - kyp->section_name = NULL; GNUNET_CONTAINER_DLL_insert (kyp_head, kyp_tail, kyp); @@ -635,21 +326,18 @@ TEH_handler_kyc_check ( return MHD_YES; } - if (TALER_EC_NONE != kyp->ec) - { - return TALER_MHD_reply_with_ec (rc->connection, - kyp->ec, - kyp->hint); - } - - /* KYC must have succeeded! */ return TALER_MHD_REPLY_JSON_PACK ( rc->connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_object_incref ("kyc_details", - kyp->kyc_details), - GNUNET_JSON_pack_timestamp ("now", - now)); + kyc_required + ? MHD_HTTP_ACCEPTED + : MHD_HTTP_OK, + GNUNET_JSON_pack_bool ("aml_review", + aml_review), + GNUNET_JSON_pack_data_auto ("access_token", + &access_token), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_steal ("limits", + jlimits))); } diff --git a/src/exchange/taler-exchange-httpd_kyc-wallet.c b/src/exchange/taler-exchange-httpd_kyc-wallet.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021, 2022 Taler Systems SA + Copyright (C) 2021, 2022, 2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -113,9 +113,6 @@ wallet_kyc_check (void *cls, MHD_RESULT *mhd_ret) { struct KycRequestContext *krc = cls; - union TALER_AccountPublicKeyP account_pub = { - .reserve_pub = krc->reserve_pub - }; return TEH_legitimization_check ( &krc->kyc, @@ -123,7 +120,6 @@ wallet_kyc_check (void *cls, mhd_ret, TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE, &krc->h_payto, - &account_pub, &balance_iterator, krc); } diff --git a/src/exchange/taler-exchange-httpd_purses_merge.c b/src/exchange/taler-exchange-httpd_purses_merge.c @@ -286,9 +286,6 @@ merge_transaction (void *cls, bool in_conflict = true; bool no_balance = true; bool no_partner = true; - union TALER_AccountPublicKeyP account_pub = { - .reserve_pub = pcc->reserve_pub - }; qs = TEH_legitimization_check ( &pcc->kyc, @@ -296,7 +293,6 @@ merge_transaction (void *cls, mhd_ret, TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE, &pcc->h_payto, - &account_pub, &amount_iterator, pcc); if ( (qs < 0) || diff --git a/src/exchange/taler-exchange-httpd_reserves_close.c b/src/exchange/taler-exchange-httpd_reserves_close.c @@ -231,11 +231,6 @@ reserve_close_transaction (void *cls, /* KYC check may be needed: we're not returning the money to the account that funded the reserve in the first place. */ - union TALER_AccountPublicKeyP account_pub = { - /* FIXME: not the correct account pub, should extract - from inbound wire transfer! Or pass NULL here? */ - .reserve_pub = *rcc->reserve_pub - }; TALER_payto_hash (rcc->payto_uri, &rcc->kyc_payto); @@ -246,7 +241,6 @@ reserve_close_transaction (void *cls, mhd_ret, TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE, &rcc->kyc_payto, - &account_pub, &amount_it, rcc); if ( (qs < 0) || diff --git a/src/exchange/taler-exchange-httpd_reserves_purse.c b/src/exchange/taler-exchange-httpd_reserves_purse.c @@ -196,9 +196,6 @@ purse_transaction (void *cls, { struct ReservePurseContext *rpc = cls; enum GNUNET_DB_QueryStatus qs; - union TALER_AccountPublicKeyP account_pub = { - .reserve_pub = *rpc->reserve_pub - }; qs = TEH_legitimization_check ( &rpc->kyc, @@ -206,7 +203,6 @@ purse_transaction (void *cls, mhd_ret, TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE, &rpc->h_payto, - &account_pub, &amount_iterator, rpc); if ( (qs < 0) || diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c @@ -42,7 +42,6 @@ TEH_legitimization_check ( MHD_RESULT *mhd_ret, enum TALER_KYCLOGIC_KycTriggerEvent et, const struct TALER_PaytoHashP *h_payto, - const union TALER_AccountPublicKeyP *account_pub, TALER_KYCLOGIC_KycAmountIterator ai, void *ai_cls) { @@ -107,18 +106,18 @@ TEH_legitimization_check ( TALER_KYCLOGIC_rule2s (requirement)); kyc->ok = false; { - json_t *jrule; + json_t *jmeasures; - jrule = TALER_KYCLOGIC_rule2j (requirement); + jmeasures = TALER_KYCLOGIC_rule_to_measures (requirement); qs = TEH_plugin->trigger_kyc_rule_for_account ( TEH_plugin->cls, h_payto, - account_pub, - jrule, + jmeasures, TALER_KYCLOGIC_rule2priority (requirement), &kyc->requirement_row); - json_decref (jrule); + json_decref (jmeasures); } + GNUNET_break (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs); if (GNUNET_DB_STATUS_HARD_ERROR == qs) { GNUNET_break (0); @@ -217,9 +216,6 @@ TEH_withdraw_kyc_check ( .withdraw_total = withdraw_total, .now = now }; - union TALER_AccountPublicKeyP account_pub = { - .reserve_pub = *reserve_pub - }; /* Check if the money came from a wire transfer */ qs = TEH_plugin->reserves_get_origin ( @@ -246,7 +242,6 @@ TEH_withdraw_kyc_check ( mhd_ret, TALER_KYCLOGIC_KYC_TRIGGER_AGE_WITHDRAW, &wc.h_payto, - &account_pub, &withdraw_amount_cb, &wc); } diff --git a/src/exchange/taler-exchange-httpd_withdraw.h b/src/exchange/taler-exchange-httpd_withdraw.h @@ -33,7 +33,6 @@ * @param[out] mhd_ret set if errors were returned * (only on hard error) * @param et type of event we are checking - * @param account_pub public key of the account * @param ai callback to get amounts involved historically * @param ai_cls closure for @a ai * @return transaction status, error will have been @@ -46,7 +45,6 @@ TEH_legitimization_check ( MHD_RESULT *mhd_ret, enum TALER_KYCLOGIC_KycTriggerEvent et, const struct TALER_PaytoHashP *h_payto, - const union TALER_AccountPublicKeyP *account_pub, TALER_KYCLOGIC_KycAmountIterator ai, void *ai_cls); diff --git a/src/exchangedb/0005-legitimization_measures.sql b/src/exchangedb/0005-legitimization_measures.sql @@ -26,7 +26,7 @@ BEGIN '(legitimization_measure_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY' ',target_token BYTEA NOT NULL CHECK (LENGTH(target_token)=32)' ',start_time INT8 NOT NULL' - ',jmeasures TEXT NOT NULL' -- FIXME: rename to jrule? + ',jmeasures TEXT NOT NULL' ',display_priority INT4 NOT NULL' ',is_finished BOOL NOT NULL DEFAULT(FALSE)' ') %s ;' @@ -57,8 +57,6 @@ BEGIN ,'legitimization_measures' ,partition_suffix ); - -- FIXME: LegitimizationMeasures is *bad* here, as we only have the KycRule; the specific measure may - -- not yet have been selected at the time of the trigger! PERFORM comment_partitioned_column( 'JSON object of type LegitimizationMeasures with KYC/AML measures for the account encoded' ,'jmeasures' diff --git a/src/exchangedb/pg_lookup_kyc_requirement_by_row.c b/src/exchangedb/pg_lookup_kyc_requirement_by_row.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022, 2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -29,9 +29,11 @@ enum GNUNET_DB_QueryStatus TEH_PG_lookup_kyc_requirement_by_row ( void *cls, uint64_t requirement_row, - char **requirements, - enum TALER_AmlDecisionState *aml_status, - struct TALER_PaytoHashP *h_payto) + union TALER_AccountPublicKeyP *account_pub, + struct TALER_AccountAccessTokenP *access_token, + json_t **jrules, + bool *aml_review, + bool *kyc_required) { struct PostgresClosure *pg = cls; uint32_t status = TALER_AML_NORMAL; @@ -55,17 +57,20 @@ TEH_PG_lookup_kyc_requirement_by_row ( PREPARE (pg, "lookup_legitimization_requirement_by_row", "SELECT " - " lr.required_checks" - ",lr.h_payto" - ",aml.status" - " FROM legitimization_requirements lr" - " LEFT JOIN aml_status aml USING (h_payto)" - " WHERE legitimization_requirement_serial_id=$1;"); - qs = GNUNET_PQ_eval_prepared_singleton_select ( + " lm.access_token" + ",lo.to_investigate AS aml_review" // can be NULL => false! + ",lo.jnew_rules AS jrules" // can be NULL! => default rules! + ",lm.is_finished AS NOT kyc_required" + ",wt.target_pub AS account_pub" // can be NULL! + " FROM legitimization_measures lm" + " JOIN wire_targets wt" + " USING (access_token)" + " LEFT JOIN legitimization_outcomes lo" + " USING (h_payto)" + " WHERE legitimization_measure_serial_id=$1;"); + return GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, "lookup_legitimization_requirement_by_row", params, rs); - *aml_status = (enum TALER_AmlDecisionState) status; - return qs; } diff --git a/src/exchangedb/pg_lookup_kyc_requirement_by_row.h b/src/exchangedb/pg_lookup_kyc_requirement_by_row.h @@ -31,17 +31,17 @@ * * @param cls closure * @param requirement_row identifies requirement to look up - * @param[out] requirements provider that must be checked - * @param[out] aml_status set to the AML status of the account - * @param[out] h_payto account that must be KYC'ed * @return database transaction status */ enum GNUNET_DB_QueryStatus TEH_PG_lookup_kyc_requirement_by_row ( void *cls, uint64_t requirement_row, - char **requirements, - enum TALER_AmlDecisionState *aml_status, - struct TALER_PaytoHashP *h_payto); + union TALER_AccountPublicKeyP *account_pub, + struct TALER_AccountAccessTokenP *access_token, + json_t **jrules, + bool *aml_review, + bool *kyc_required); + #endif diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h @@ -52,6 +52,11 @@ /** + * Account owner signature for KYC. + */ +#define TALER_HTTP_HEADER_ACCOUNT_OWNER_SIGNATURE "Account-Owner-Signature" + +/** * Possible algorithms for confirmation code generation. */ enum TALER_MerchantConfirmationAlgorithm @@ -232,6 +237,37 @@ union TALER_AccountPublicKeyP /** + * @brief Type of signatures made by merchants. + */ +struct TALER_MerchantSignatureP +{ + /** + * Taler uses EdDSA for merchants. + */ + struct GNUNET_CRYPTO_EddsaSignature eddsa_sig; +}; + + +/** + * @brief Type of signatures for KYC authorizations. + * Either a merchant's signature or a reserve signature + * will do. + */ +union TALER_AccountSignatureP +{ + /** + * Signature of merchants. + */ + struct TALER_MerchantSignatureP merchant_sig; + + /** + * Signature of reserves. + */ + struct TALER_ReserveSignatureP reserve_sig; +}; + + +/** * @brief Type of private keys for merchant authorizations. * Merchants can issue refunds using the corresponding * private key. @@ -265,18 +301,6 @@ union TALER_AccountPrivateKeyP /** - * @brief Type of signatures made by merchants. - */ -struct TALER_MerchantSignatureP -{ - /** - * Taler uses EdDSA for merchants. - */ - struct GNUNET_CRYPTO_EddsaSignature eddsa_sig; -}; - - -/** * @brief Type of transfer public keys used during refresh * operations. */ diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h @@ -6721,8 +6721,7 @@ struct TALER_EXCHANGEDB_Plugin * * @param cls closure * @param h_payto account that must be KYC'ed - * @param account_pub public key authorizing access, NULL if not known - * @param jrule serialized KYC rule that was triggered + * @param jrule serialized MeasureSet to put in place * @param display_priority priority of the rule * @param[out] requirement_row set to legitimization requirement row for this check * @return database transaction status @@ -6731,7 +6730,6 @@ struct TALER_EXCHANGEDB_Plugin (*trigger_kyc_rule_for_account)( void *cls, const struct TALER_PaytoHashP *h_payto, - const union TALER_AccountPublicKeyP *account_pub, const json_t *jrule, uint32_t display_priority, uint64_t *requirement_row); @@ -6801,25 +6799,23 @@ struct TALER_EXCHANGEDB_Plugin struct GNUNET_TIME_Absolute expiration); -#if 0 /** * Lookup KYC requirement. * * @param cls closure * @param legi_row identifies requirement to look up - * @param[out] requirements space-separated list of requirements - * @param[out] aml_status set to the AML status of the account - * @param[out] h_payto account that must be KYC'ed * @return database transaction status */ enum GNUNET_DB_QueryStatus (*lookup_kyc_requirement_by_row)( void *cls, uint64_t requirement_row, - char **requirements, - enum TALER_AmlDecisionState *aml_status, - struct TALER_PaytoHashP *h_payto); -#endif + union TALER_AccountPublicKeyP *account_pub, + struct TALER_AccountAccessTokenP *access_token, + json_t **jrules, + bool *aml_review, + bool *kyc_required); + /** * Lookup KYC process meta data. diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h @@ -350,10 +350,6 @@ const char * TALER_KYCLOGIC_rule2s (const struct TALER_KYCLOGIC_KycRule *r); -json_t * -TALER_KYCLOGIC_rule2j (const struct TALER_KYCLOGIC_KycRule *r); - - uint32_t TALER_KYCLOGIC_rule2priority (const struct TALER_KYCLOGIC_KycRule *r); @@ -386,6 +382,43 @@ TALER_KYCLOGIC_is_satisfiable ( /** + * Check if any KYC checks are enabled. + * + * @return true if KYC is enabled + * false if no KYC checks are possible + */ +bool +TALER_KYCLOGIC_is_enabled (void); + + +/** + * A KYC rule @a r has been triggered. Convert the resulting requirements in + * to JSON of type ``LegitimizationMeasures`` for the legitimization measures table. + * + * FIXME: not implemented! + * @param r a rule that was triggered + * @return JSON serialization of the corresponding + * ``LegitimizationMeasures``, NULL on error + */ +json_t * +TALER_KYCLOGIC_rule_to_measures (const struct TALER_KYCLOGIC_KycRule *r); + + +/** + * Convert (internal) @a jrules to (public) @a jlimits. + * + * @param jrules a ``LegitimizationRuleSet`` with KYC rules; + * NULL to use default rules + * @return set to JSON array with public limits + * of type ``AccountLimit`` + * + * FIXME: not implemented! + */ +json_t * +TALER_KYCLOGIC_rules_to_limits (const json_t *jrules); + + +/** * Extract logic data from a KYC @a provider. * * @param provider provider to get logic data from diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c @@ -76,6 +76,12 @@ struct TALER_KYCLOGIC_KycRule char *rule_name; /** + * Rule set with custom measures that this KYC rule + * is part of. FIXME: not initialized yet! + */ + const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs; + + /** * Timeframe to consider for computing the amount * to compare against the @e limit. Zero for the * wallet balance trigger (as not applicable). @@ -433,7 +439,13 @@ TALER_KYCLOGIC_rule2s (const struct TALER_KYCLOGIC_KycRule *r) json_t * -TALER_KYCLOGIC_rule2j (const struct TALER_KYCLOGIC_KycRule *r) +TALER_KYCLOGIC_rules_to_limits (const json_t *jrules) +{ +} + + +json_t * +TALER_KYCLOGIC_rule_to_measures (const struct TALER_KYCLOGIC_KycRule *r) { // FIXME! GNUNET_break (0); @@ -1535,6 +1547,19 @@ TALER_KYCLOGIC_kyc_init (const struct GNUNET_CONFIGURATION_Handle *cfg) } +/** + * Check if any KYC checks are enabled. + * + * @return true if KYC is enabled + * false if no KYC checks are possible + */ +bool +TALER_KYCLOGIC_is_enabled (void) +{ + return 0 != num_kyc_providers; +} + + void TALER_KYCLOGIC_kyc_done (void) {